8. Hata Yakalama¶
Bu bölümde yine Python Programlama Dili ile ilgili çok önemli bir konudan söz edeceğiz. Yeni konumuz Python’da Hata Yakalama. Bu bölümde Python’da hatalarla başetmeyi öğreneceğiz...
Hatırlarsanız önceki bölümlerde yazdığımız programlarda, örneğin kullanıcı bizim beklentimizin aksine bir sayı yerine harf girerse programımız hata verip çöküyordu. Gelin isterseniz buna basit bir örnek verelim:
Amacımız iki sayıyı toplayan bir program yazmak olsun:
topla1 = input("Lütfen toplama işlemine girecek ilk sayıyı giriniz: ")
topla2 = input("Lütfen toplama işlemine girecek ikinci sayıyı giriniz: ")
sonuç = int(topla1) + int(topla2)
print(sonuç)
Bu programı çalıştırdığımızda, programımız kullanıcı tarafından girilen iki sayıyı toplayıp ekrana yazdıracaktır. Burada işlerin doğru gitmesi kullanıcının ekrana iki adet sayı yazmasına bağlıdır. Eğer kullanıcı sayı yerine harf girerse Python’un bize vereceği çıktı şuna benzer bir şey olacaktır:
Traceback (most recent call last):
File "deneme.py", line 5, in <module>
sonuç = int(topla1) + int(topla2)
ValueError: invalid literal for int() with base 10: 'f'
Esasında daha önceki bir bölümde örnek olarak basit bir hesap makinesi yazarken de böyle bir sorunumuzun olduğunu söylemiştik. Orada aynı zamanda sonraki bir derste bu sorunun kesin çözümünü öğreneceğimizi de söylemiştik. İşte bu tür problemlerin çözümünü şimdi inceleyeceğimiz bölümde öğreneceğiz...
8.1. try... except...¶
Python’da hatalarla başetmek için try... except... adlı bloklardan yararlanacağız. Bu blokları Türkçe olarak söylememiz gerekirse şöyle diyebiliriz: “dene... kabul_et...”. Buradan anlayabileceğimiz gibi, öncelikle Python’a bir şey deneteceğiz. Bu deneme esnasında bir hata meydana gelirse, Python bu hatayı kabul edecek, yani sineye çekecek ve hiçbir şey olmamış gibi yoluna devam edecektir. İsterseniz bu sözlerimizi bir örnekle süsleyelim.
Geçen bölümde şöyle bir örnek vermiştik:
topla1 = input("Lütfen toplama işlemine girecek ilk sayıyı giriniz: ")
topla2 = input("Lütfen toplama işlemine girecek ikinci sayıyı giriniz: ")
sonuç = int(topla1) + int(topla2)
print(sonuç)
Bu programı çalıştırdığımızda eğer kullanıcı sırasıyla iki tane sayı girerse ne ala!... Programımız hiç bir sorun çıkartmadan bu sayıları toplayacaktır. Ama eğer kullanıcı sayı girmek yerine, mesela “a” harfine basarsa programımızın şöyle bir hata vereceğini biliyoruz:
Traceback (most recent call last):
File "deneme.py", line 5, in <module>
sonuç = int(topla1) + int(topla2)
ValueError: invalid literal for int() with base 10: 'a'
Bu hata mesajı bizim için önemli bazı bilgiler içeriyor. Burada bizim dikkat etmemiz gereken, bu hatanın son satırı... Görüyoruz ki hata mesajının son satırında “ValueError” ifadesi var. İşte hata mesajının bizim işimize yarayacak kısmı bu. Demek ki kullanıcı sayı yerine harf girerse Python’un verdiği hata türü “ValueError” imiş... Şimdi şöyle bir şey yazalım:
try:
topla1 = input("Lütfen toplama işlemine girecek ilk sayıyı giriniz: ")
topla2 = input("Lütfen toplama işlemine girecek ikinci sayıyı giriniz: ")
sonuç = int(topla1) + int(topla2)
print(sonuç)
except ValueError:
print("Yanlış değer!")
Artık kullanıcımız sayı dışında bir değer girerse, programımız hata vermek yerine daha anlamlı bir çıktı üretecek ve kullanıcıya “Yanlış değer!” uyarısı gösterecektir. Burada yaptığımız şey, programın bütün aşamalarını bir try... bloğu içine almak.. Kullanıcının sayı dışı bir veri girmesi durumunda “ValueError” ile karşılaşacağımızı biliyoruz. Bu hata türünü “except ValueError” şeklinde gösterip, Python’un bu hata türünü kabul etmesini istiyoruz. Aslında burada Python’a şu emri vermiş oluyoruz:
“Kullanıcıya, birbiriyle toplanacak sayıları sor! Daha sonra bu sayıları topla ve sonucu ekrana yaz! Eğer “try” bloğu içinde gerçekleşen işlemlerin herhangi bir aşamasında “ValueError” hatası oluşursa pat diye çökmek yerine ekrana “Yanlış değer!” ifadesini yazdır!”
İsterseniz try.. except.. bloğunun işlevini daha net görebilmek için yukarıdaki örneğimizi bir while döngüsü içine alalım. Böylece programımız tekrar tekrar çalışsın:
while True:
topla1 = input("Lütfen toplama işlemine girecek ilk sayıyı giriniz: ")
topla2 = input("Lütfen toplama işlemine girecek ikinci sayıyı giriniz: ")
sonuç = int(topla1) + int(topla2)
print(sonuç)
Gördüğünüz gibi, bu şekilde programımız her işlem sonunda başa dönüp kullanıcıya tekrar sayı soruyor. Tabii eğer kullanıcı harf yerine başka bir veri girerse programımızın çalışması sona eriyor. Bir de şuna bakalım:
while True:
try:
topla1 = input("Lütfen toplama işlemine girecek ilk sayıyı giriniz: ")
topla2 = input("Lütfen toplama işlemine girecek ikinci sayıyı giriniz: ")
sonuç = int(topla1) + int(topla2)
print(sonuç)
except ValueError:
print("yanlış değer!")
Programımızı çalıştırdığımızda, kullanıcı ne tür bir veri girerse girsin programımız çalışmasına devam edecektir. Eğer kullanıcı iki adet sayı girerse bunları toplayıp sonucu ekrana yazdıracak; ama eğer kullanıcı sayı yerine başka bir şey girerse de önce kullanıcıyı yanlış değer girdiği konusunda uyaracak ve yine çalışmasına devam edecektir.
Bu demek oluyor ki, try... except... bloğu sayesinde programımız “ValueError” hatasıyla karşılaşsa bile çökmeden çalışmaya devam edebilecektir.
Buradan, Python’da hata yakalama konusunun önemli bir faydasını öğrenmiş oluyoruz: Bildiğiniz gibi, Python’u kendi haline bıraktığınızda, kullanıcı için pek bir şey ifade etmeyecek hata mesajları üretiyor. Bu mesajlar bir Python programcısı için çok şey ifade ediyor olsa da, programımızı kullanan kimselerin bu “kriptik” hata mesajlarını anlamasını bekleyemeyiz. O yüzden onlara bu tür bulanık hata mesajları yerine, yukarıda gördüğümüz try... except... bloklarını kullanarak daha anlamlı mesajlar gösterebiliriz... Ne de olsa “yanlış değer!” gibi bir ileti, “ValueError: invalid literal for int() with base 10: ‘a’” gibi, oldukça bilgisayarvari bir hata mesajına göre çok daha anlamlı olacaktır son kullanıcı için.
İsterseniz daha önce yaptığımız “basit hesap makinesi”ne geri dönüp, yukarıda öğrendiğimiz yeni bilgiyi o kodlara uygulayalım. Zira hesap makinemiz de, eğer kullanıcı sayı yerine harf girerse çöküyordu:
#!/usr/bin/env python3.0
while True:
giriş = """
(1) topla
(2) çıkar
(3) çarp
(4) böl
(5) karesini hesapla
Programdan çıkmak için "ç" harfine basınız...
"""
print(giriş)
soru = input("Yapmak istediğiniz işlemin numarasını girin: ")
try:
if soru == "1":
sayı1 = int(input("Toplama işlemi için ilk sayıyı girin: "))
sayı2 = int(input("Toplama işlemi için ikinci sayıyı girin: "))
print(sayı1, "+", sayı2, "=", sayı1 + sayı2)
elif soru == "2":
sayı3 = int(input("Çıkarma işlemi için ilk sayıyı girin: "))
sayı4 = int(input("Çıkarma işlemi için ikinci sayıyı girin: "))
print(sayı3, "-", sayı4, "=", sayı3 - sayı4)
elif soru == "3":
sayı5 = int(input("Çarpma işlemi için ilk sayıyı girin: "))
sayı6 = int(input("Çarpma işlemi için ikinci sayıyı girin: "))
print(sayı5, "x", sayı6, "=", sayı5 * sayı6)
elif soru == "4":
sayı7 = int(input("Bölme işlemi için ilk sayıyı girin: "))
sayı8 = int(input("Bölme işlemi için ikinci sayıyı girin: "))
print(sayı7, "/", sayı8, "=", sayı7 / sayı8)
elif soru == "5":
sayı9 = int(input("Karesini hesaplamak istediğiniz sayıyı giriniz: "))
print(sayı9, "sayısının karesi =", sayı9 ** 2)
elif soru == "ç":
print("Tekrar görüşmek üzere!")
a = 0
else:
print("Yanlış giriş.")
print("Aşağıdaki seçeneklerden birini giriniz:")
except ValueError:
print("Sadece sayı giriniz!")
Gördüğünüz gibi, burada da yaptığımız şey, kullanıcının sayı yerine harf girmesi durumunda Python’un “ValueError” hatası vereceğini bildiğimiz kodları try... bloğu içine almaktan ibaret... “ValueError” hatası alındığında kullanıcıya gösterilecek mesajı ise “except ValueError” bloğu içinde gösteriyoruz.
Yukarıda gördüğümüz try... except... blokları konusunda önemli bir nokta vardır dikkat etmemiz gereken... “except ValueError” şeklinde bir ifade kullandığımızda, Python yalnızca “ValueError” hatalarını yakalayacaktır. Bir program “ValueError” dışında da pek çok hata verebilir. Mesela yukarıda tekrar verdiğimiz hesap makinemizin bir sorunu daha var. Bu programda bölme işlemi sırasında bir sayıyı 0’a bölmeye çalışırsak programımız şöyle bir hata verecektir:
Traceback (most recent call last):
File "deneme.py", line 38, in <module>
print(sayı7, "/", sayı8, "=", sayı7 / sayı8)
ZeroDivisionError: int division or modulo by zero
Programlama dillerinde bir sayının sıfıra bölünmesi programın çökmesiyle sonuçlanır. O yüzden biz de yazdığımız programlarda yapılan işlemlerin bir aşamasında herhangi bir sayının sıfıra bölünmediğinden emin olmalıyız...
Yukarıdaki hata mesajında bizi ilgilendiren kısım “ZeroDivisionError”. Dolayısıyla hata mesajını yakalarken bu ifadeyi kullanacağız. Yazacağımız kodun taslağı şöyle olacak:
try:
...hata vereceğini bildiğimiz kodlar...
except ZeroDivisionError:
...hata geldiğinde kullanıcıya gösterilecek mesaj veya yapılacak işlem...
Bu kodları, yukarıda “ValueError” için yazdığımız kodların yerine koyabiliriz. Bu basit bir iş. Peki ya aynı program içinde hem “ValueError” hatasını, hem de “ZeroDivisionError” hatasını yakalamak istersek ne yapacağız? Bu daha da basit bir iş. Taslağımız şöyle:
try:
...hata vereceğini bildiğimiz kodlar...
except ValueError:
...ValueError hatası aldığımızda yapacağımız işlem...
except ZeroDivisionError:
...ZeroDivisionError hatası aldığımızda yapacağımız işlem...
Gördüğünüz gibi, birden fazla hatayı aynı anda yakalamak istediğimizde, her bir hata için ayrı bir except... bloğu yazmamız yeterli oluyor. Elbette, yukarıdaki taslak, her bir hata mesajı için ayrı bir işlem yapmak istediğimizde geçerli. Eğer hangi hatayla karşılaşılırsa karşılaşılsın aynı mesaj gösterilecekse veya aynı işlem yapılacaksa kodlarımızı şöyle de yazabiliriz:
try:
...hata vereceğini bildiğimiz kodlar...
except (ValueError, ZeroDivisionError):
...hata alınınca yapılacak işlem...
Burada hata mesajlarını bir demet içinde topladığımıza özellikle dikkat edin.
Bir sonraki bölümde bu “hata yakalama” konusunu incelemeye devam edeceğiz. Ama öncelikle öğrenmemiz gereken başka şeyler var...
8.2. “break” Deyimi¶
Python’da break özel bir deyimdir. Bu deyim yardımıyla, devam eden bir süreci kesintiye uğratabiliriz. Bu deyimin kullanıldığı basit bir örnek verelim:
>>> tekrar = 1
>>> while tekrar <= 10:
... tekrar = tekrar + 1
... try:
... soru = int(input("öğrencinin notu: "))
... except ValueError:
... break
Burada öncelikle “tekrar” adlı bir değişken tanımladık. Bu değişkenin değeri “1”. “tekrar” adlı değişkenin değeri 10’dan küçük veya 10’a eşit olduğu müddetçe programımızın çalışmaya devam etmesi için “while tekrar <=10” şeklinde bir satır yazıyoruz. Daha sonra, tanımladığımız bu while döngüsü içinde, “tekrar” adlı değişkenin değerini her döngüde 1 sayı artırıyoruz. Böylece programımız “tekrar” adlı değişkenin değeri 10’a ulaşıncaya kadar çalışmaya devam edecek. Ardından, “soru = int(input(“öğrencinin notu: ”))” ifadesi yardımıyla bir dönüştürme işlemi yapıyoruz. input() fonksiyonu ile kullanıcıdan beklediğimiz verinin tipi “sayı” olacak. Kullanıcının sayı yerine bir harf girmesi ihtimaline karşı input() fonksiyonunu bir try... bloğu içine yerleştirdik. Eğer kullanıcı sayı yerine harf girerse programımızın “ValueError” hatası vereceğini biliyoruz. O yüzden bir except... bloğu oluşturarak bu olası hatayı yakalıyoruz. break deyimi bu noktada devreye giriyor. Eğer kullanıcı “ValueError” hatasının verilmesine yol açacak bir veri girerse programımız sessizce sonlanacaktır... Bu sessizce sonlanma işini yerine getiren kodumuz en son satırdaki break ifadesi...
Programı çalıştırdığımızda, eğer kullanıcı sayı yerine bir harf girerse, veya hiç bir veri girmeden “enter” tuşuna basarsa programımız kapanacaktır.
Gördüğünüz gibi, break ifadesinin temel görevi bir döngüyü sona erdirmek. Buradan anlayacağımız gibi, break ifadesinin her zaman bir döngü içinde yer alması gerekiyor. Aksi halde Python bize şöyle bir hata verecektir:
SyntaxError: 'break' outside loop
Yani; “SözDizimiHatası: “break” döngü dışında“
Bununla ilgili basit bir örnek daha verelim:
>>> while True:
... parola = input("Lütfen bir parola belirleyiniz:")
... if len(parola) < 5:
... print("Parola 5 karakterden az olmamalı!")
... else:
... print("Parolanız belirlendi!")
... break
Burada da, eğer kullanıcının girdiği parolanın uzunluğu 5 karakterden azsa, “Parola 5 karakterden az olmamalı!” uyarısı gösterilecektir. Eğer kullanıcı 5 karakterden uzun bir parola belirlemişse, kendisine “Parolanız belirlendi!” mesajını gösterip, break deyimi yardımıyla programdan çıkıyoruz...
8.3. “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:
>>> liste = []
>>> while True:
... a = input("Herhangi bir karakter giriniz: ")
... if a == "0":
... pass
... else:
... liste.append(a)
... print(liste)
Burada eğer kullanıcının girdiği karakter “0” ise hiçbir şey yapmıyoruz. Ama eğer kullanıcı “0” dışında herhangi bir karakter girerse, bu karakterleri alıp listeye ekliyoruz. Bu kodlarla Python’a şöyle bir şey söylemiş oluyoruz:
Eğer kullanıcı “0” karakterini girerse görmezden gel. Hiçbir şey yapma!... Ama eğer girilen karakter “0” dışında bir şeyse bunu listeye ekle ve listeyi ekrana bas!
pass deyimini hata yakalama işlemlerinde de kullanabiliriz. Eğer bir hata yakalandığında programımızın hiç bir şey yapmadan yoluna devam etmesini istiyorsak bu deyim işimize yarayacaktır:
try:
....bir şeyler...
except IndexError:
pass
Bu şekilde programımız “IndexError” hatasıyla karşılaşırsa hiç bir şey yapmadan 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...
8.4. “continue” Deyimi¶
continue ilginç bir deyimdir. İsterseniz continue‘yi anlatmaya çalışmak yerine bununla ilgili bir örnek verelim:
>>> while True:
... s = input("Bir sayı girin: ")
... if s == "iptal":
... break
... if len(s) <= 3:
... continue
... print("En fazla üç haneli bir sayı girebilirsiniz.")
Burada eğer kullanıcı klavyede “iptal” yazarsa programdan çıkılacaktır. Bunu;
if s == "iptal":
break
satırıyla sağlamayı başardık.
Eğer kullanıcı tarafından girilen sayı üç haneli veya daha az haneli bir sayı ise, continue ifadesinin etkisiyle:
>>> print("En fazla üç haneli bir sayı girebilirsiniz.")
satırı es geçilecek ve döngünün en başına gidilecektir.
Eğer kullanıcının girdiği sayıdaki hane üçten fazlaysa ekrana:
En fazla üç haneli bir sayı girebilirsiniz.
cümlesi yazdırılacaktır.
Dolayısıyla buradan anladığımıza göre, continue deyiminin görevi kendisinden sonra gelen her şeyin es geçilip döngünün başına dönülmesini sağlamaktır. Bu bilgiye göre, yukarıdaki programda eğer kullanıcı, uzunluğu 3 karakterden az bir sayı girerse continue deyiminin etkisiyle programımız döngünün en başına geri gidiyor. Ama eğer kullanıcı, uzunluğu 3 karakterden fazla bir sayı girerse, ekrana “En fazla üç haneli bir sayı girebilirsiniz,” cümlesinin yazdırıldığını görüyoruz.
8.5. else... finally...¶
Python’da hata yakalama işlemleri için çoğunlukla try... except... bloklarını bilmek yeterli olacaktır. İşlerimizin büyük kısmını sadece bu blokları kullanarak halledebiliriz. Ancak Python bize bu konuda, zaman zaman işimize yarayabilecek başka araçlar da sunmaktadır. İşte else... finally blokları da bu araçlardan biridir. Bu bölümde kısaca else... ve finally... bloklarının ne işe yaradığından söz edeceğiz.
Öncelikle else... bloğunun ne işe yaradığına bakalım. Esasında biz bu else deyimini daha önce de “koşullu ifadeler” konusunu işlerken görmüştük. Buradaki kullanımı da zaten hemen hemen aynıdır. Diyelim ki elimizde şöyle bir şey var:
try:
bölünen = int(input("bölünecek sayı: "))
bölen = int(input("bölen sayı: "))
print(bölünen/bölen)
except ValueError:
print("hata!")
Burada eğer kullanıcı sayı yerine harf girerse “ValueError” hatası alırız. Bu hatayı “except ValueError:” ifadesiyle yakalıyoruz ve hata verildiğinde kullanıcıya uyarı vererek programımızın çökmesini engelliyoruz. Ama biliyoruz ki, bu kodları çalıştırdığımızda Python’un verebileceği tek hata “ValueError” değildir. Eğer kullanıcı bir sayıyı 0’a bölmeye çalışırsa Python “ZeroDivisionError” adlı hatayı verecektir. Dolayısıyla bu hatayı da yakalamak için şöyle bir şey yazabiliriz:
try:
bölünen = int(input("bölünecek sayı: "))
bölen = int(input("bölen sayı: "))
print(bölünen/bölen)
except ValueError:
print("hata!")
except ZeroDivisionError:
print("Bir sayıyı 0'a bölemezsiniz!")
Bu şekilde hem “ValueError” hatasını hem de “ZeroDivisionError” hatasını yakalamış oluruz... Bu kodların özelliği, except... bloklarının tek bir try... bloğunu temel almasıdır. Yani biz burada bütün kodlarımızı tek bir try... bloğu içine tıkıştırıyoruz. Bu blok içinde gerçekleşen hataları da daha sonra tek tek except... blokları yardımıyla yakalıyoruz. Ama eğer biz istersek bu kodlarda verilebilecek hataları gruplamayı da tercih edebiliriz:
try:
bölünen = int(input("bölünecek sayı: "))
bölen = int(input("bölen sayı: "))
except ValueError:
print("hata!")
else:
try:
print(bölünen/bölen)
except ZeroDivisionError:
print("Bir sayıyı 0'a bölemezsiniz!")
Burada yaptığımız şey şu: İlk try... except... bloğu yardımıyla öncelikle “int(input())” fonksiyonu ile kullanıcıdan gelecek verinin sayı olup olmadığını denetliyoruz. Ardından bir “else...” bloğu açarak, bunun içinde ikinci try... except... bloğumuzu devreye sokuyoruz. Burada da bölme işlemini gerçekleştiriyoruz. Kullanıcının bölme işlemi sırasında “0” sayısını girmesi ihtimaline karşı da “except ZeroDivisionError” ifadesi yardımıyla olası hatayı göğüslüyoruz... Bu şekilde bir kodlamanın bize getireceği avantaj, hatalar üzerinde belli bir kontrol sağlamamıza yardımcı olmasıdır. Yukarıdaki kodlar sayesinde hatalara bir nevi “teker teker gelin!” mesajı vermiş oluyoruz!... Böylelikle her blok içinde sadece almayı beklediğimiz hatayı karşılıyoruz. Mesela yukarıda ilk try... bloğu içindeki dönüştürme işlemi yalnızca “ValueError” hatası verebilir. else: bloğundan sonraki try... bloğunda yer alan işlem ise ancak “ZeroDivisionError” verecektir. Biz yukarıda kullandığımız yapı sayesinde her bir hatayı tek tek ve yeri geldiğinde karşılıyoruz. Bu durumun aksine, bölümün ilk başında verdiğimiz try... bloğunda hem “ValueError” hem de “ZeroDivisionError” hatalarının gerçekleşme ihtimali bulunuyor. Dolayısıyla biz orada bütün hataları tek bir try... bloğu içine sıkıştırmış oluyoruz. İşte else: bloğu bu sıkışıklığı gidermiş oluyor. Ancak sizi bir konuda uyarmak isterim: Bu yapı, her akla geldiğinde kullanılacak bir yapı değildir. Büyük programlarda bu tarz bir kullanım kodlarınızın darmadağın olmasına, kodlarınız üzerindeki denetimi tamamen kaybetmenize yol açabilir. Sonunda da elinizde bölük pörçük bir kod yığını kalabilir... Zaten açıkça söylemek gerekirse try... except... else... yapısının çok geniş bir kullanım alanı yoktur. Bu yapı ancak çok nadir durumlarda kullanılmayı gerektirebilir. Dolayısıyla bu üçlü yapıyı hiç kullanmadan bir ömrü rahatlıkla geçirebilirsiniz...
try... except... else... yapılarının dışında, Python’un bize sunduğu bir başka yapı da try... except... finally... yapılarıdır... Bunu şöyle kullanıyoruz:
try:
...bir takım işler...
except birHata:
...hata alınınca yapılacak işlemler...
finally:
...hata olsa da olmasa da yapılması gerekenler...
finally.. bloğunun en önemli özelliği, programın çalışması sırasında herhangi bir hata gerçekleşse de gerçekleşmese de işletilecek olmasıdır. Eğer yazdığınız programda mutlaka ama mutlaka işletilmesi gereken bir kısım varsa, o kısmı finally... bloğu içine yazabiliriz.
finally... bloğu özellikle dosya işlemlerinde işimize yarayabilir. Henüz Python’da dosyalarla nasıl çalışacağımızı öğrenmedik, ama ben şimdilik size en azından dosyalarla çalışma prensibi hakkında bir şeyler söyleyeyim.
Genel olarak Python’da dosyalarla çalışabilmek için öncelikle bilgisayarda bulunan bir dosyayı okuma veya yazma kipinde açarız. Dosyayı açtıktan sonra bu dosyayla ihtiyacımız olan birtakım işlemler gerçekleştiririz. Dosyayla işimiz bittikten sonra ise dosyamızı mutlaka kapatmamız gerekir. Ancak eğer dosya üzerinde işlem yapılırken bir hata ile karşılaşılırsa dosyamızı kapatma işlemini gerçekleştirdiğimiz bölüme hiç ulaşılamayabilir. İşte finally... bloğu böyle bir durumda işimize yarayacaktır:
try:
dosya = open("dosyaadı", "r")
...burada dosyayla bazı işlemler yapıyoruz...
...ve ansızın bir hata oluşuyor...
except IOError:
print("bir hata oluştu!")
finally:
dosya.close()
Burada finally... bloğu içine yazdığımız “dosya.close()” ifadesi dosyamızı kapatmaya yarıyor. Bu blok, yazdığımız program hata verse de vermese de işletilecektir.
8.6. except... as...¶
- Python’daki hata mesajları temel olarak iki bölümden oluşur:
- Hatanın adı,
- Hatanın adı ile birlikte gösterilen mesaj.
Bir örnek üzerinde görelim bunu:
ValueError: invalid literal for int() with base 10: 'a'
Burada “ValueError” hatanın adıdır. “invalid literal for int() with base 10: ‘a’” ise gösterilen mesaj... Biz eğer istersek bu hatanın adını özelleştirebilir, o kısma istediğimiz başka bir ifade yazabiliriz. Bunun için except... as... bloklarından yararlanacağız. Hemen bir örnek verelim:
try:
bölünen = int(input("bölünecek sayı: "))
bölen = int(input("bölen sayı: "))
print(bölünen/bölen)
except ZeroDivisionError as hata:
print("Sıfıra Bölme Hatası. Python şu hata mesajını verdi:", hata)
Burada “ZeroDivisionError” adlı hatayı aslında bileşenlerine ayırmış olduk... “except ZeroDivisionError as hata” ifadesi yardımıyla bu hata mesajının ana gövdesini “hata” olarak adlandırdık. Son olarak da bu “hata” değişkenini print() fonksiyonu içinde kullanarak ekrana yazdırdık.
8.7. raise¶
Bazen, yazdığımız bir programda, kullanıcının yaptığı bir işlem normal şartlar altında hata vermeyecek olsa bile biz ona “Python tarzı” bir hata mesajı göstermek isteyebiliriz. Böyle bir durumda ihtiyacımız olan şey Python’un bize sunduğu raise adlı deyimdir. Bu deyim yardımıyla duruma özgü hata mesajları üretebiliriz. Bir örnek verelim:
bölünen = int(input("bölünecek sayı: "))
if bölünen == 23:
raise Exception("Bu programda 23 sayısını görmek istemiyorum!")
bölen = int(input("bölen sayı: "))
print(bölünen/bölen)
Burada eğer kullanıcı “23” sayısını girerse, kullanıcıya bir hata mesajı gösterilip programdan çıkılacaktır... Biz bu kodlarda “Exception” adlı genel hata mesajını kullandık. Burada “Exception” yerine her istediğimizi yazamayız. Yazabileceklerimiz ancak Python’da tanımlı hata mesajları olabilir. Örneğin NameError, TypeError, ZeroDivisionError, IOError, vb...
raise deyimini, bir hata mesajına ek olarak bir işlem yapmak istediğimizde de kullanabiliriz. Örneğin:
try:
bölünen = int(input("bölünecek sayı: "))
bölen = int(input("bölen sayı: "))
print(bölünen/bölen)
except ZeroDivisionError:
print("bir sayıyı 0'a bölemezsiniz")
raise
Burada, eğer kullanıcı bir sayıyı 0’a bölmeye çalışırsa, normal bir şekilde “ZeroDivisionError” hatası verilecek ve programdan çıkılacaktır. Ama bu hata mesajıyla birlikte kullanıcıya “bir sayıyı 0’a bölemezsiniz” uyarısını da gösterme imkanını elde edeceğiz... Yani burada “except ZeroDivisionError” bloğunu herhangi bir hatayı engellemek için değil, hataya ilave bilgi eklemek için kullanıyoruz. Bunu yapmamızı sağlayan şey tabii ki bu kodlar içinde görünen raise adlı deyimdir...
8.8. Bütün Hataları Yakalamak¶
Şimdiye kadar yaptığımız bütün örneklerde except... bloğunu bir hata mesajı adıyla birlikte kullandık. Yani örneklerimiz şuna benziyordu:
try:
....birtakım işler...
except ZeroDivisionError:
...hata mesajı...
Yukarıdaki kod yardımıyla sadece “ZeroDivisionError” adlı hatayı yakalayabiliriz. Eğer yazdığımız program başka bir hata daha veriyorsa, o hata mesajı yukarıdaki blokların kapsamı dışında kalacaktır. Ama eğer istersek yukarıdaki kodu şu şekilde yazarak olası bütün hataları yakalayabiliriz:
try:
....birtakım işler...
except:
...hata mesajı...
Gördüğünüz gibi, burada herhangi bir hata adı belirtmedik. Böylece Python, yazdığımız programda hangi hata oluşursa oluşsun hepsini yakalayabilecektir.
Bu yöntem gözünüze çok pratik görünmüş olabilir, ama aslında hiç de öyle sayılmaz. Hatta oldukça kötü bir yöntem olduğunu söyleyebiliriz bunun. Çünkü bu tarz bir kod yazımının bazı dezavantajları vardır. Örneğin bu şekilde bütün hata mesajlarını aynı kefeye koyarsak, programımızda ne tür bir hata oluşursa oluşsun, kullanıcıya hep aynı mesajı göstermek zorunda kalacağız. Bu da, herhangi bir hata durumunda kullanıcıyı ne yapması gerektiği konusunda doğru düzgün bilgilendiremeyeceğimiz anlamına geliyor. Yani kullanıcı bir hataya sebep olduğunda tersliğin nereden kaynaklandığını tam olarak kestiremeyecektir.
Ayrıca, eğer kendimiz bir program geliştirirken sürekli olarak bu tarz bir yazımı benimsersek, kendi kodlarımızdaki hataları da maskelemiş oluruz. Dolayısıyla, Python yukarıdaki geniş çaplı except... bloğu nedeniyle programımızdaki bütün hataları gizleyeceği için, programımızdaki potansiyel aksaklıkları görme imkanımız olmaz. Dolayısıyla bu tür bir yapıdan olabildiğince kaçınmakta fayda var...