Python (9. Bölüm)
Pardus-Linux.org | Wiki sitesinden
Forum'da bu konunun tartışma adresi: http://forum.pardus-linux.org/viewtopic.php?t=10478&highlight=
Konu başlıkları |
Hatalarla Başetme
Giriş
Programcılar bir kod yazarken, yazılan kodları işletecek kullanıcıları her zaman göz önünde bulundurmalı, program çalıştırılırken kullanıcıların ne gibi hatalar yapabileceklerini kestirmeye çalışmalıdır. Çünkü kullanıcılar her zaman programcının istediği gibi davranmayabilir. Bu sözleri basit bir örnekle açıklayalım. Diyelim ki bir kod yazdık ve kullanıcıdan bir sayı girmesini istiyoruz. Eğer kullanıcı gerçekten bir sayı girerse sorun yok, ancak tabii ki kullanıcıların her zaman uslu uslu sayı girmesini bekleyemeyiz ve beklememeliyiz. Çünkü siz her ne kadar açık açık sayı girilmesini isteseniz de kullanıcı bilerek veya bilmeyerek sayı yerine başka değerler de girebilir. Hatta hiç bir giriş yapmadan "enter" tuşuna bile basabilir. Yazdığımız bu 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.
Değişken İsmine İlişkin Hatalar (NameError)
Şu örneğe bir bakalım:
#!/usr/bin/env python
#-*- coding: latin-1 -*-
while True:
soru1 = input("Lütfen toplama işlemi için bir sayı giriniz: ")
soru2 = input("Lütfen toplama işlemi için ikinci sayıyı giriniz: ")
print soru1 + soru2
Eğer iyi huylu bir kullanıcıya denk gelirsek ne âlâ... Sevgili kullanıcımız uslu uslu iki adet sayı girecek, programımız da mutlu mesut bir şekilde bu iki sayıyı toplayıp kibarca kullanıcıya bildirecektir... Ama ne yazık ki işler her zaman böyle yürümez... Kullanıcımız programı çalıştırdıktan sonra bir sayı yerine bir harf girmeyi de tercih edebilir. Böyle bir durumda ise kullanıcı şu hatayı alır. (Diyelim ki kullanıcı "e" harfine basmış olsun):
Traceback (most recent call last):
File "deneme.py", line 4, in ?
soru1 = input("Lütfen toplama işlemi için bir sayı giriniz: ")
File "<string>", line 0, in ?
NameError: name 'e' is not defined
İşte bu noktadan sonra kullanıcı, program yazarının kulaklarını çınlatmaya başlayacak, "Ne biçim program bu. Hemen çöküyor," diye sızlanacaktır. Gerçi aldığı hata mesajı sorunun nerede olduğunu söylüyor, ama tabii ki anlayana... Sizin bu noktada iyi bir programcı olarak yapmanız gereken şey, kullanıcının hareketlerini önceden kestirip, onun alacağı hata mesajlarını anlaşılır hale getirmek olacaktır.
Şimdi ortaya çıkan hata mesajına bir bakalım. Bu mesajda önemli kısım "NameError: name 'e' is not defined" yazan yer... Demek ki Python, kullanıcının girdiği "e" harfini değişken olarak algılamış, tabii ki ortada tanımlanmış bir "e" değişkeni olmadığı için de böyle bir hata mesajı vermiş... Bu sorunu şu şekilde giderebiliriz:
#!/usr/bin/env python
#-*- coding: latin-1 -*-
while True:
try:
soru1 = input("Lütfen toplama işlemi için bir sayı giriniz: ")
soru2 = input("Lütfen toplama işlemi için ikinci sayıyı giriniz: ")
print soru1 + soru2
except NameError:
print "Sayı dedik sana! Harf değil! Tekrar dene..."
Artık kullanıcı sayı yerine harf girdiğinde, programımız çökmeyecek, sayı girmesi konusunda kullanıcıyı nazikçe uyararak çalışmaya devam edecektir.
İsterseniz burada yaptığımız şeyi biraz açıklayalım.. Aslında yaptığımız şey, yazdığımız ilk kodları bir "try... except..." bloğu içine almaktan ibaret. Yazdığımız bu kod ile Python'a kendi anlayacağı dilden şöyle demiş olduk:
"Eğer programın çalıştırılması sırasında değişken ismine ilişkin bir hatayla karşılaşırsan bu hatayı sineye çek ve ekrana, 'Sayı dedik sana!...' cümlesini yazdırıp yoluna devam et..."
Biz yukarıdaki kodla kullanıcıların yapabileceği bir hata türüyle başetmiş olduk, ama emin olun kullanıcılar çok daha başka, çok daha karmaşık hatalar da yapabilirler...
Sözdizimine İlişkin Hatalar (SyntaxError)
Dediğimiz gibi, kullanıcıların yapabileceği hataların sınırı, hududu yok... Mesela yukarıdaki kodu çalıştıran bir başka kullanıcı sadece sayı girmek yerine, önce bir sayı girip, "enter"e basmadan bir tane de harf girmeyi uygun görebilir... Yani "3g" gibi bir şey yazabilir... O zaman da şöyle bir hata alır:
Traceback (most recent call last):
File "deneme.py", line 5, in ?
soru1 = input("Lütfen toplama işlemi için bir sayı giriniz: ")
File "<string>", line 1
3g
^
SyntaxError: unexpected EOF while parsing
Burada da önemli kısım, "SyntaxError: unexpected EOF while parsing"... Buradan anladığımıza göre, Python bir "sözdizimi hatası" vermiş... Bu hatayı da kodumuza şu şekilde ekleyebiliriz:
#!/usr/bin/env python
#-*- coding: latin-1 -*-
while True:
try:
soru1 = input("Lütfen toplama işlemi için bir sayı giriniz: ")
soru2 = input("Lütfen toplama işlemi için ikinci sayıyı giriniz: ")
print soru1 + soru2
except NameError:
print "Sayı dedik sana! Harf değil! Tekrar dene..."
except SyntaxError:
print "Yazım hatası yaptınız! Lütfen bir daha deneyin..."
Böylelikle kullanıcıdan kaynaklanabilecek iki hata türünü öngörüp her biri için ayrı uyarı verebiliyoruz.
Hata Kodu Vermeden Hata Yakalama
Tabii ki bir kullanıcının yol acabileceği bütün hataları kestirmek mümkün değildir. O yüzden, eğer hataya yönelik özel bir mesaj göstermek gibi bir kaygımız yoksa olası bütün hatalar için şu kalıbı kullanabiliriz:
try: ..... except: .....
Hemen bir örnek verelim:
try:
dosya = open("deneme.txt","r")
except:
print "dosya açılamıyor"
print 2 + 2
Gördüğünüz gibi burada herhangi bir hata kodu belirtmedik. Ama olası bir hatayı yakaladığımız için programımız çökmedi ve bir sonraki kod olan "print 2+2" işlemi yapıldı. İsterseniz aynı kodu bir de "try...except..." bloğu olmadan deneyelim:
dosya = open("deneme.txt","r")
print 2 + 2
Burada ise, herhangi bir hata yakalama işlemi yapmadığımız için programımız çöktü ve ikinci kod olan "print 2 + 2" işletilemedi...
Yukarıda anlattığımız, "hata kodu vermeden hata yakalama işlemi" pratik ve kolay olsa bile her zaman tercih edilmeyebilir. Çünkü bu şekilde kullanıcıya yaptığı hatayla ilgili bilgi veremiyoruz. Dolayısıyla bu kodları içeren bir programı çalıştıran kullanıcı ortada bir hata olduğunu anlayacak, ama hatanın nereden kaynaklandığını bilemeyecektir. Çünkü yukarıda, "dosya açılamıyor" diye bir hata belirttik ama hata kodu yazmadığımız için bu dosyanın neden açılamadığını belirtemedik... Zira yukarıdaki kodda dosyanın açılamamasının birkaç nedeni olabilir. Ama eğer kodumuzu şöyle verirsek en azından kullanıcının bazı önlemler almasını sağlayabiliriz.. Yukarıdaki kodu çalıştırdığımızda şöyle bir hata almıştık:
Traceback (most recent call last):
File "deneme.py", line 3, in ?
dosya = open("deneme.txt","r")
IOError: [Errno 2] No such file or directory: 'deneme.txt'
Şimdi hatayı tespit ettiğimize göre şu kodu yazabiliriz:
try:
dosya = open("deneme.txt","r")
except IOError:
print "'deneme.txt' adlı dosya bulunamadı. Lütfen klasörde bu adda bir dosya olduğundan emin olunuz."
print 2 + 2
Böylelikle kullanıcıya daha açıklayıcı bir bilgi vermiş olduk. Artık kullanıcı hatanın ne olduğunu bildiği için buna karşı önlem de alabilir. Bir de, yukarıda görünen hata mesajında dikkat ederseniz [Errno 2] diye bir ifade geçiyor. Bunun ne olduğunu anlamak için şu örneği verelim:
dosya = open("/usr/bin/deneme.txt","w")
Bu kodu çalıştırdığımızda şu hatayı alırız:
Traceback (most recent call last):
File "deneme.py", line 3, in ?
dosya = open("/usr/bin/deneme.txt", "w")
IOError: [Errno 13] Permission denied: '/usr/bin/deneme.txt'
Gördüğünüz gibi burada da "IOError" adlı hata veriliyor, ama bu kez hata kodu [Errno 13].
Eğer istersek, biz her iki hata kodu için ayrı mesajlar verebiliriz kullanıcıya:
try:
dosya = open("/usr/bin/deneme.txt","r")
except IOError, (hatakodu, hataadi):
if hatakodu == 2:
print "Böyle bir dosya yok"
if hatakodu == 13:
print "Bu dosyayı okuma yetkiniz yok"
Burada dikkat ederseniz (hatakodu, hataadi) adında iki adet parametre oluşturduk. Bu isimleri tabii ki siz kendinize göre de belirleyebilirsiniz. Önemli olan, parantez içinde iki ayrı parametre olması... Daha sonra da "if ifadeleri" yardımıyla her iki koşul için ekrana yazdırılacak çıktıları belirledik...
Hatalarla Başetmede "pass" İfadesi"
Bazen yazdığınız program hata verse bile siz kullanıcıya herhangi bir hata mesajı göstermek istemeyebilirsiniz. Böyle bir durumda kullanıcının sebep olduğu hata sessizce geçiştirilecek, programınız çalışmaya devam edecektir.
Bir örnek verelim:
#!/usr/bin/env python
#-*- coding: latin-1 -*-
liste = ["elma", "armut", "karpuz", "kavun", "erik", "üzüm", "şeftali", "muz"]
while True:
try:
s = raw_input("Lütfen bir meyve adı söyleyiniz: ")
p = liste.index(s) + 1
print s, "listemizde", p, "no'lu sırada bulunuyor"
except ValueError:
pass
Burada öncelikle, içeriğinde bazı meyveler olan bir liste yarattık. Ardından da kullanıcılardan, "bir meyve adı söylemelerini" istedik. Daha önceki bölümlerden hatırlayacağınız "liste.index()" fonksiyonunu kullanarak kullanıcının girdiği meyve adının listede kaçıncı sırada olduğunu sorguladık. Bildiğiniz gibi Python'da liste öğeleri sıralanırken hep sıfırdan başlanıyor... Python'un kendi iç yapısı açısından bu durum mantıklı olabilir, ama insanlar saymaya bir'den başlamayı daha mantıklı bulacakları için biz kodumuza "+1" değerini ekleyerek Python'un listedeki öğeleri sıralamaya 0'dan değil de 1'den başlamasını sağladık. Bunun ardından da, kullanıcının girdiği değerin listede kaçıncı sırada olduğunu ekrana yazdırdık. Tabii ki kullanıcı isim girerken, listede olmayan bir öğeyi de söyleyebilir. Böyle bir durumda programımızın bu hatayı sessiz sedasız geçiştirmesi için de "pass" ifadesini kullandık. Eğer "try...except..." yapısını kullanmasaydık ne olacağını biliyorsunuz:
Traceback (most recent call last):
File "deneme.py", line 8, in ?
p = liste.index(s) + 1
ValueError: list.index(x): x not in list
Gördüğünüz gibi, kullanıcı açısından tamamen anlamsız bir kelime yığını çıkıyor ortaya... Üstelik programımız da bu noktada işlevini kaybedip çöküyor...
Önceki Bölüm: Dosya İşlemleri
Sonraki Bölüm: Karakter Dizilerinin Metotları
Bu yazı Programlama bölümünün bir parçasıdır.
