Python - İstisnai Durum Yönetimi
Programı yazdıktan sonra çalışma zamanında oluşabilecek sonuçlar beklenmedik sonuçlara yol açabilir. Birçok dilde olduğu gibi Python'da da hata yakalama mekanizmaları mevcut. Hata oluşturması muhtemel kod parçalarını try-except blokları içine alarak hata oluşması durumunda programa alternatif bir yol gösterebiliriz.
Hata Yakalama
Riskli kodları try
bloğuna, hata oluşması durumunda işlenecek kodları ise except
bloğuna yazarız.Örnek bir kod aşağıda.
# coding: utf8 dogum = raw_input('Doğum yılı: ') try: yas = 2015 - int(dogum) print('Yaşın: {}'.format(yas)) except: print('Olamaz... Hata oluştu!')
Doğum yılı hatalı (string değer) girildiğinde hata oluşan satırda programı sonlandırmak yerine except
bloğunu işledik.
Bu örnekte try
bloğunda oluşan hataya dair hiçbir fikrimiz yok. Şimdi hatanın except
bloğuna düşmesini sağlayalım.
# coding: utf8 dogum = raw_input('Doğum yılı: ') try: yas = 2015 - int(dogum) print('Yaşın: {}'.format(yas)) except Exception as e: print(dir(e))
Her hata Exception
sınıfından türetilmiş birer objedir. Yukarıdaki örnekte oluşan hatanın e
isminde bloğa düşmesini sağlayıp dir
fonksiyonu ile sınıf içindeki elemanları listeledik.
Hata içindeki message
değişkeni hata metnini barındırır. İstersek kullanıcıya hatanın ne olduğunu söyleyebiliriz.
# coding: utf8 dogum = raw_input('Doğum yılı: ') try: yas = 2015 - int(dogum) print('Yaşın: {}'.format(yas)) except Exception as e: print('Hata oluştu: {}'.format(e.message))
Anlatsana biraz, neden ben?
Yakalamak istediğimiz hata türlerini sınırlandırabiliriz. Tüm hataların Exception
sınıfından türetildiğini söylemiştik. Bunun için fark gözetmeden tüm hataları yakalıyoruz. Şimdi bir değişiklik yapalım ve sadece girilen değer rakam olmadığı zaman hata vermesini sağlayalım.
# coding: utf8 rakam = raw_input('10 kaça bölünsün: ') try: sonuc = 10 / int(rakam) print('Sonuç: {}'.format(sonuc)) except ValueError as e: print('Hata: Lütfen bir sayı gir, metin değil.')
Yukarıdaki örnekte bir metni sayıya çevirmeye çalışırken oluşan ValueError
tipini yakaladık. Yani sayı girdiğiniz sürece hiçbir hata oluşmayacak. Ancak girdiye 0 (sıfır) girdiğinizde göreceksiniz ki ne bizim hata mesajımızı gösterecek ne de program çalışacaktır. Gösterilen hata metninden anlayabileceğiniz üzere oluşan hata ZeroDivisionError
. Şimdi buna göre kodumuzu düzenleyelim.
# coding: utf8 rakam = raw_input('10 kaça bölünsün: ') try: sonuc = 10 / int(rakam) print('Sonuç: {}'.format(sonuc)) except ValueError as e: print('Hata: Lütfen bir sayı gir, metin değil.') except ZeroDivisionError as e: print('Hata: Hiçbir sayı sıfıra bölünemez. :(')
Evet, şimdi daha mantıklı oldu. Fark edeceğiniz üzere birden fazla hata tipi için birden fazla except
bloğu kullanabiliyoruz.
Hatalıysam Arayın
İstediğimiz (daha doğrusu istemediğimiz) durumlarda biz de hata oluşturabiliriz. Python'da bu işi raise
anahtar kelimesi ile yaparız. Mesela aşağıdaki örneğe bakalım.
# coding: utf8 try: ad = raw_input('Adın ne: ') if not ad: raise Exception('İsmini girmeliydin.') except Exception as e: print(e.message)
Exception sınıfını yükseltirken (raise etmek) kullandığımız hata metnine except
bölümünde message
değişkeni ile ulaşabiliyoruz. Ya da aşağıdaki gibi bir örnek olabilir.
# coding: utf8 try: isim = raw_input('Kullanıcı adı: ') parola = raw_input('Parola: ') if not isim == 'umut' or not parola == '1234': raise Exception() print('Hoşgeldin, {}.'.format(isim)) except Exception as e: print('bizimle deyılsın.')
Kullanıcı adı "umut", parolası "1234" olmyan kişilerin hata mesajıyla karşılaşmasını sağladık. Bu arada ön tanımlı hata türleri hakkında bilgi edinmek isterseniz sizi şuraya alalım.
Velev ki hatasızım?
Bloklar içinde hata oluşmadığı zamanlarda else
bloğunun işlenmesini sağlayabiliriz. Mesela yukarıdaki örneği bir de bu şekilde yapalım.
# coding: utf8 try: isim = raw_input('Kullanıcı adı: ') parola = raw_input('Parola: ') if not isim == 'umut' or not parola == '1234': raise Exception() except Exception as e: print('bizimle deyılsın.') else: print('Hoşgeldin, {}.'.format(isim))
Son Olarak...
Yok, yazı sona ermedi. Tüm bu blokların haricinde hata oluşsa da, oluşmasa da işletilecek bir blok tanımlayabiliriz. Bunu finally
anahtar kelimesi ile yaparız. Şöyle ki;
# coding: utf8 try: isim = raw_input('Kullanıcı adı: ') parola = raw_input('Parola: ') if not isim == 'umut' or not parola == '1234': raise Exception() except Exception as e: print('Bizimle deyılsın.') else: print('Hoşgeldin, {}.'.format(isim)) finally: print('İyi kötü anılarımız oldu, artık gidebilirsin.')
Şimdi diyebilirsiniz; "Madem her halukarda işletilecek, o zaman except bloğundan sonra yazarız. Ne gerek var buna?" diye. Ben de demiştim cevabını şurada buldum.
Diyelim ki bir fonksiyon yazdınız ve try ve except bloklarında ayrı ayrı return
ifadeleri var. İşte bu finally bloğu, fonksiyondan değer döndürülmeden hemen önce çalıştırılıyor. Eğer except bloğunun altına yazsaydık tabi ki de program return ifadesini gördüğü yerde fonksiyonu sonlandırıp değeri döndürücekti.
Aşağıdaki kodda hata oluştuğunda dosyanın bellekten silinmesi sağlanmış.
# coding: utf8 dosya = open("merhaba.txt", "w") try: dosya.write(007) finally: dosya.close()
Herkesin Hatası Kendine!
Öntanımlı hatalar artık size yetmediğinde kendi hata türlerinizi Exception
sınıfından türeterek kullanabilirsinz. Örneğin aşağıda girilen isim geçersiz(!) olduğunda hata mesajı verdirdik.
# coding: utf8 class GecersizAd(Exception): def __init__(self, message): self.message = message def __str__(self): return self.message try: isim = raw_input('Adın ne: ') if len(isim) < 3 : raise GecersizAd('Böyle isim olmaz olsun. İsim dediğin en az üç harfli olur.') except GecersizAd as e: print(e.message)
Son olarak (bu sefer gerçek), işlem yapmak istemediğiniz blokları pass
anahtar kelimesi ile geçebilirsiniz. Boş bırakamazsınız, hata verir. Mesela;
# coding: utf8 try: x = raw_input('Ne?: ') print(x / 2) except: pass
Ama pek önerilen birşey değildir. Dilerseniz Logger sınıfı ile hataları kaydedip daha sonra inceleyebilirsiniz.
Kolay gelsin!