Hata Yakalama¶
Hatalar programcılık deneyiminizin bir parçasıdır. Programcılık hayatınız boyunca hatalarla sürekli muhatab olacaksınız. Ancak bizim burada kastettiğimiz, programı yazarken sizin yapacağınız hatalar değil. Kastettiğimiz şey, programınızı çalıştıran kullanıcıların sebep olduğu ve programınızın çökmesine yol açan kusurlar.
Dilerseniz ne demek istediğimizi anlatmak için şöyle bir örnek verelim:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
sayi = int(raw_input("Lütfen bir sayı girin: "))
print "Girdiğiniz sayı", sayi
print "Teşekkürler. Hoşçakalın!"
Gayet basit bir program bu, değil mi? Bu program görünüşte herhangi bir kusur taşımıyor. Eğer kullanıcımız burada bizim istediğimiz gibi bir sayı girerse bu programda hiçbir sorun çıkmaz. Programımız görevini başarıyla yerine getirir. Peki ama ya kullanıcı sayı yerine harf girerse ne olacak? Mesela kullanıcı yukarıdaki programı çalıştırıp “a” harfine basarsa ne olur?
Böyle bir durumda programımız şöyle bir hata mesajı verir:
Traceback (most recent call last):
File "deneme.py", line 4, in <module>
sayi = int(raw_input("Lütfen bir sayı girin: "))
ValueError: invalid literal for int() with base 10: 'a'
Gördüğünüz gibi, ortaya çıkan hata nedeniyle programın son satırı ekrana basılamadı. Yani programımız hata yüzünden çöktüğü için işlem yarıda kaldı.
Burada böyle bir hata almamızın nedeni, kullanıcıdan sayı beklediğimiz halde kullanıcının harf girmesi. Biz yukarıdaki programda int() fonksiyonunu kullanarak kullanıcıdan aldığımız karakter dizilerini sayıya dönüştürmeye çalışıyoruz. Mesela kullanıcı “23” değerini girerse bu karakter dizisi rahatlıkla sayıya dönüştürülebilir. Dilerseniz bunu etkileşimli kabukta test edelim:
>>> int("23")
23
Gördüğünüz gibi “23” adlı karakter dizisi rahatlıkla sayıya dönüştürülebiliyor. Peki ya şu?
>>> int("a")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'a'
İşte bizim programımızın yapmaya çalıştığı şey de tam olarak budur. Yani programımız kullanıcıdan aldığı “a” harfini sayıya dönüştürmeye çalışıyor ve tabii ki başarısız oluyor.
Bir kod yazarken, yazılan kodları işletecek kullanıcıları her zaman göz önünde bulundurmanız gerektiğini asla unutmayın. Program çalıştırılırken kullanıcıların ne gibi hatalar yapabileceklerini kestirmeye çalışın. Çünkü kullanıcılar her zaman sizin istediğiniz gibi davranmayabilir. Siz programın yazarı olarak, kodlarınızı tanıdığınız için programınızı nasıl kullanmanız gerektiğini biliyorsunuzdur, ama son kullanıcı böyle değildir.
Yukarıdaki kodun, çok uzun bir programın parçası olduğunu düşünürsek, kullanıcının yanlış veri girişi koskoca bir programın çökmesine veya durmasına yol açabilir. Bu tür durumlarda Python gerekli hata mesajını ekrana yazdırarak kullanıcıyı uyaracaktır, ama tabii ki Python’un sunduğu karmaşık hata mesajlarını kullanıcının anlamasını bekleyemeyiz. Böylesi durumlar için Python’da try... except ifadeleri kullanılır. İşte biz de bu bölümde bu tür ifadelerin ne zaman ve nasıl kullanılacağını anlamaya çalışacağız.
try... except...¶
Dediğimiz gibi, Python’da hata yakalamak için try... except bloklarından yararlanılır. Gelin isterseniz bunun nasıl kullanılacağına bakalım.
Giriş bölümünde verdiğimiz örneği hatırlıyorsunuz. O örnek, kullanıcının sayı yerine harf girmesi durumunda hata veriyordu. Şimdi hata mesajına tekrar bakalım:
Traceback (most recent call last):
File "deneme.py", line 4, in <module>
sayi = int(raw_input("Lütfen bir sayı girin: "))
ValueError: invalid literal for int() with base 10: 'a'
Burada önemli kısım ValueError‘dur. Hatayı yakalarken bu ifade işimize yarayacak.
Python’da hata yakalama işlemleri iki adımdan oluşur. Önce ne tür bir hata ortaya çıkabileceği tespit edilir. Daha sonra bu hata meydana geldiğinde ne yapılacağına karar verilir. Yukarıdaki örnekte biz birinci adımı uyguladık. Yani ne tür bir hata ortaya çıkabileceğini tespit ettik. Buna göre, kullanıcı sayı yerine harf girerse ValueError denen bir hata meydana geliyormuş... Şimdi de böyle bir hata ortaya çıkarsa ne yapacağımıza karar vermemiz gerekiyor. Mesela öyle bir durumda kullanıcıya, “Lütfen harf değil, sayı girin!” gibi bir uyarı mesajı gösterebiliriz. Dilerseniz bu dediklerimizi somutlaştıralım. Kodlarımızın ilk hali şöyleydi:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
sayi = int(raw_input("Lütfen bir sayı girin: "))
print "Girdiğiniz sayı", sayi
print "Teşekkürler. Hoşçakalın!"
Bu tarz bir kodlamanın hata vereceğini biliyoruz. Şimdi şuna bakın:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
try:
sayi = int(raw_input("Lütfen bir sayı girin: "))
print "Girdiğiniz sayı", sayi
except ValueError:
print "Lütfen harf değil, sayı girin!"
print "Teşekkürler. Hoşçakalın!"
Burada, hata vereceğini bildiğimiz kodları bir try... bloğu içine aldık. Ardından bir except bloğu açarak, ne tür bir hata beklediğimizi belirttik. Buna göre, beklediğimiz hata türü ValueError. Son olarak da hata durumunda kullanıcıya göstereceğimiz mesajı yazdık. Artık kullanıcılarımız sayı yerine harfe basarsa programımız çökmeyecek, aksine çalışmaya devam edecektir. Dikkat ederseniz print "Teşekkürler. Hoşçakalın!" satırı her koşulda ekrana basılıyor. Yani kullanıcı doğru olarak sayı da girse, yanlışlıkla sayı yerine harf de girse programımımız yapması gereken işlemleri tamamlayıp yoluna devam edebiliyor.
Konuyu daha iyi anlayabilmek için bir örnek daha verelim. Hatırlarsanız bir sayıyı 0’a bölmenin hata olduğunu söylemiştik. Şimdi şöyle bir program yazdığımızı düşünün:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
ilk = int(raw_input("Bölme işlemi için ilk sayıyı girin: "))
ikinci = int(raw_input("Şimdi de ikinci sayıyı girin: "))
sonuc = float(ilk) / ikinci
print sonuc
Eğer burada kullanıcı ikinci sayıya 0 cevabı verirse programımız şöyle bir hata mesajı verip çökecektir:
Traceback (most recent call last):
File "deneme.py", line 7, in <module>
sonuc = float(ilk) / ikinci
ZeroDivisionError: float division
Böyle bir durumda hata alacağımızı bildiğimize göre ilk adım olarak ne tür bir hata mesajı alabileceğimizi tespit ediyoruz. Buna göre alacağımız hatanın türü ZeroDivisionError. Şimdi de böyle bir hata durumunda ne yapacağımıza karar vermemiz gerekiyor. İsterseniz yine kullanıcıya bir uyarı mesajı gösterelim.
Kodlarımızı yazıyoruz:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
ilk = int(raw_input("Bölme işlemi için ilk sayıyı girin: "))
ikinci = int(raw_input("Şimdi de ikinci sayıyı girin: "))
try:
sonuc = float(ilk) / ikinci
print sonuc
except ZeroDivisionError:
print "Lütfen sayıyı 0'a bölmeye çalışmayın!"
Bir önceki örnekte de yaptığımız gibi burada da hata vereceğini bildiğimiz kodları try... bloğu içine aldık. Böyle bir durumda alınacak hatanın ZeroDivisionError olduğunu da bildiğimiz için except bloğunu da buna göre yazdık. Son olarak da kullanıcıya gösterilecek uyarıyı belirledik. Böylece programımız hata karşısında çökmeden yoluna devam edebildi.
Burada önemli bir problem dikkatinizi çekmiş olmalı. Biz yukarıdaki kodlarda, kullanıcının bir sayıyı 0’a bölmesi ihtimaline karşı ZeroDivisionError hatasını yakaladık. Ama ya kullanıcı sayı yerine harf girerse ne olacak? ZeroDivisionError ile birlikte ValueError‘u da yakalamamız gerekiyor... Eğer yukarıdaki kodları çalıştıran bir kullanıcı sayı yerine harf girerse şöyle bir hatayla karşılaşır:
Traceback (most recent call last):
File "deneme.py", line 4, in <module>
ilk = int(raw_input("Bölme işlemi için ilk sayıyı girin: "))
ValueError: invalid literal for int() with base 10: 'a'
Buradan anladığımıza göre hata veren satır şu: ilk = int(raw_input("Bölme işlemi için ilk sayıyı girin: ")) Dolayısıyla bu satırı da try... bloğu içine almamız gerekiyor.
Şu kodları dikkatlice inceleyin:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
try:
ilk = int(raw_input("Bölme işlemi için ilk sayıyı girin: "))
ikinci = int(raw_input("Şimdi de ikinci sayıyı girin: "))
sonuc = float(ilk) / ikinci
print sonuc
except ZeroDivisionError:
print "Lütfen sayıyı 0'a bölmeye çalışmayın!"
except ValueError:
print "Lütfen harf değil, sayı girin!"
Gördüğünüz gibi hata vereceğini bildiğimiz kodların hepsini bir try... bloğu içine aldık. Ardından da verilecek hataları birer except bloğu içinde teker teker yakaladık. Eğer her iki hata için de aynı mesajı göstermek isterseniz şöyle bir şey yazabilirsiniz:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
try:
ilk = int(raw_input("Bölme işlemi için ilk sayıyı girin: "))
ikinci = int(raw_input("Şimdi de ikinci sayıyı girin: "))
sonuc = float(ilk) / ikinci
print sonuc
except (ZeroDivisionError, ValueError):
print "Girdiğiniz veri hatalı!"
Hata türlerini nasıl grupladığımıza dikkat edin. Hataları birbirinden virgülle ayırıp parantez içine alıyoruz... Böylece programımız her iki hata türü için de aynı uyarıyı gösterecek kullanıcıya.
Bu bölüm, hata yakalama konusuna iyi bir giriş yapmamızı sağladı. İlerde çok daha farklı hata türleriyle karşılaştığınızda bu konuyu çok daha net bir şekilde içinize sindirmiş olacaksınız.
İsterseniz şimdi bu konuyla bağlantılı olduğunu düşündüğümüz, önemli bir deyim olan pass‘i inceleyelim.
pass Deyimi¶
pass kelimesi İngilizce’de “geçmek” anlamına gelir. Bu deyimin Python programlama dilindeki anlamı da buna çok yakındır. Bu deyimi Pyhon’da “görmezden gel, hiçbir şey yapma” anlamında kullanacağız. Mesela bir hata ile karşılaşan programınızın hiçbir şey yapmadan yoluna devam etmesini isterseniz bu deyimi kullanabilirsiniz:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
try:
ilk = int(raw_input("Bölme işlemi için ilk sayıyı girin: "))
ikinci = int(raw_input("Şimdi de ikinci sayıyı girin: "))
sonuc = float(ilk) / ikinci
print sonuc
except (ZeroDivisionError, ValueError):
pass
Böylece programınız ZeroDivisionError veya ValueError ile karşılaştığında sessizce yoluna devam edecek, böylece kullanıcımız programda ters giden bir şeyler olduğunu dahi anlamayacaktır. Yazdığınız programlarda bunun iyi bir şey mi yoksa kötü bir şey mi olduğuna programcı olarak sizin karar vermeniz gerekiyor. Eğer bir hatanın kullanıcıya gösterilmesinin gerekmediğini düşünüyorsanız yukarıdaki kodları kullanın, ama eğer verilen hata önemli bir hataysa ve kullanıcının bu durumdan haberdar olması gerektiğini düşünüyorsanız, bu hatayı pass ile geçiştirmek yerine, kullanıcıya hatayla ilgili makul ve anlaşılır bir mesaj göstermeyi düşünebilirsiniz.
Yukarıda anlatılan durumların dışında, pass deyimini kodlarınız henüz taslak aşamasında olduğu zaman da kullanabilirsiniz. Örneğin, diyelim ki bir kod yazıyorsunuz. Programın gidişatına göre, bir noktada yapmanız gereken bir işlem var, ama henüz ne yapacağınıza karar vermediniz. Böyle bir durumda pass deyiminden yararlanabilirsiniz. Mesela birtakım if deyimleri yazmayı düşünüyor olun:
if .....:
böyle yap
elif .....:
şöyle yap
else:
pass
Burada henüz else bloğunda ne yapılacağına karar vermemiş olduğunuz için, oraya bir pass koyarak durumu şimdilik geçiştiriyorsunuz. Program son haline gelene kadar oraya bir şeyler yazmış olacağız.
Sözün özü, pass deyimlerini, herhangi bir işlem yapılmasının gerekli olmadığı durumlar için kullanıyoruz. İlerde işe yarar programlar yazdığınızda, bu pass deyiminin göründüğünden daha faydalı bir araç olduğunu anlayacaksınız.
Bölüm Soruları¶
1. Eğer yazdığınız bir programda, kullanıcının yapabileceği olası hataları önceden kestirip bunları yakalamazsanız ne gibi sonuçlarla karşılaşırsınız?
2. Daha önce yazdığımız basit hesap makinesini, programı kullanan kişilerin yapabileceği hatalara karşı önlem alarak yeniden yazın.
3. Python’da şöyle bir şey yazmak mümkündür:
try:
bir takım işler
except:
print "bir hata oluştu!"
Burada dikkat ederseniz herhangi bir hata türü belirtmedik. Bu tür bir kod yazımında, ortaya çıkan bütün hatalar yakalanacak, böylece kullanıcıya bütün hatalar için tek bir mesaj gösterilebilecektir. İlk bakışta gayet güzel bir özellik gibi görünen bu durumun sizce ne gibi sakıncaları olabilir?