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!