8. Hatalar ve İstisnalar
- 8.1. Sözdizimi Hataları
- 8.2. İstisnalar
- 8.3. İstisnaların Ele Alınması
- 8.4. İstisna Oluşturma
- 8.5. Kullanıcı Tanımlı İstisnalar
- 8.6. Son İşlemlerin Belirlenmesi
Şu ana kadar hata mesajlarından pek bahsedilmedi; ancak örnekleri denediyseniz muhtemelen birkaç tane görmüşsünüzdür. Birbirinden ayırt edilebilen en az iki tür hata mevcuttur: sözdizim hataları ve istisnalar.
8.1. Sözdizimi Hataları
Sözdizimi hataları ayrıştırma (parsing) hataları olarak da bilinirler ve Python öğrenirken en çok bunlar ile karşılaşırsınız:
>>> while 1 print 'Merhaba'
File "<stdin>", line 1, in ?
while 1 print 'Merhaba'
^
SyntaxError: invalid syntax
Ayrıştırıcı sorun olan satırı basar ve satır içinde hatanın algılandığı
ilk noktayı küçük bir `ok' ile gösterir. Hata oktan önce gelen kısımdan
kaynaklanmaktadır. Örnekte hata print anahtar
kelimesinde fark edilmektedir; çünkü ondan önce bir iki nokta üst üste
(":") karakteri eksiktir. Dosya adı ve satır numarası da yazdırılmaktadır
ki yorumlayıcı girişinin bir dosyadan gelmesi durumunda hatanın nereden
kaynaklandığını bilesiniz.
8.2. İstisnalar
Bir deyim ya da ifade sözdizimsel olarak doğru olsa da yürütülmek istendiğinde bir hataya sebep olabilir. İcra sırasında meydana gelen hatalara istisna denir. İstisnaları nasıl ele alabileceğinizi yakında öğreneceksiniz. Çoğu istisnalar yazılımlar tarafından ele alınmaz ve aşağıdakiler gibi hata mesajları ile sonuçlanırlar:
>>>10 * (1/0) Traceback (most recent call last): File "<stdin>", line 1, in ? ZeroDivisionError: integer division or modulo>>>4 + spam*3 Traceback (most recent call last): File "<stdin>", line 1, in ? NameError: spam>>>'2' + 2 Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: illegal argument type for built-in operation
Hata mesajının son satırı sorunun ne olduğunu belirtir. İstisnaların
farklı türleri vardır ve istisnanın türü hata mesajının bir bölümü olarak
yazdırılır. Örneklerdeki istisna türleri: ZeroDivisionError,
NameError ve TypeError. İstisna türü
olarak yazdırılan dizge meydana gelen istisnanın yerleşik ismidir.
Bu bütün yerleşik istisnalar için geçerlidir; ancak kullanıcı tanımlı
istisnalar için böyle olmayabilir. Standart istisna isimleri yerleşik
belirteçlerdir; ayrılmış anahtar kelimeler değil.
Satırın devamı istisna türüne bağlı ayrıntılardan oluşur ve anlamı istisna türüne bağlıdır.
Hata mesajının baş kısmında istisnanın meydana geldiği yer yığın dökümü şeklinde görülür. Bu genellikle istisnanın gerçekleştiği noktaya gelene kadar işletilen kaynak kodu şeklinde olur; ancak standart girdiden okunan satırlar gösterilmez.
Yerleşik istisnalar ve bunların anlamları için Python ile gelen belgelerden yararlanılabilir.
8.3. İstisnaların Ele Alınması
Belirli istisnaları ele alan yazılımlar yazmak mümkündür. Aşağıdaki örnek,
kullanıcıdan geçerli bir tamsayı girilene kadar kullanıcıdan giriş yapması
istenir. Control-C tuş kombinasyonu (ya da işletim sisteminin desteklediği
başka bir kombinasyon) ile kullanıcı yazılımdan çıkabilir. Kullanıcın
sebep olduğu bu olay ise KeyboardInterrupt istisnasının
oluşmasına neden olur.
>>>while True:...try:...x = int(raw_input("Lütfen bir rakam giriniz: "))...break...except ValueError:...print "Bu geçerli bir giriş değil. Tekrar deneyin..."...
try deyimi aşağıdaki gibi çalışır:
-
Önce
trybloğu (tryveexceptarasındaki ifade(ler)) işletilir. -
Hiçbir istisna oluşmaz ise
exceptbloğu atlanır vetrydeyimin icrası son bulur. -
Eğer
trybloğu içinde bir istisna oluşur ise bloğun geri kalanı atlanır. İstisnanın türüexceptanahtar kelimesinden sonra kullanılan ile aynı isetrybloğunun kalan kısmı atlanır veexceptbloğu yürütülür. Programın akışıtry ... exceptkısmından sonra gelen ilk satırdan devam eder. -
Adı
exceptbloğunda geçmeyen bir istisna oluşur ise üst seviyedekitryifadelerine geçirilir; ancak bunu ele alan bir şey bulunmaz ise bu bir ele alınmamış istisna olur ve yürütme işlemi yukarıda da görüldüğü gibi bir hata mesajı ile son bulur.
Bir try deyimi farklı istisnaları yakalayabilmek için
birden fazla except bloğuna sahip olabilir. Bir
except bloğu parantez içine alınmış bir liste ile
birden fazla istisna adı belirtebilir. Örnek:
...except (RuntimeError, TypeError, NameError):...pass
Son except bloğu istisna adı belirtilmeden de
kullanılıp herhangi bir istisnayı yakalayabilir. Bunu çok dikkatli
kullanın, çünkü çok ciddi bir yazılımlama hatasını bu şekilde gözden
kaçırabilirsiniz! Bu özellik bir hata mesajı bastırıp ve tekrar bir
istisna oluşturarak çağıranın istisnayı ele almasını da sağlamak için
kullanılabilir:
import string, sys
try:
f = open('myfile.txt')
s = f.readline()
i = int(string.strip(s))
except IOError, (errno, strerror):
print "I/O error(%s): %s" % (errno, strerror)
except ValueError:
print "Could not convert data to an integer."
except:
print "Unexpected error:", sys.exc_info()[0]
raise
try ... except ifadesinin seçimlik
else bloğu da vardır. Bu her
except bloğunun ardına yazılır ve
try bloğunun istisna oluşturmadığı durumlarda
icra edilmesi gereken kod bulunduğu zaman kullanılır. Örnek:
for arg in sys.argv[1:]:
try:
f = open(arg, 'r')
except IOError:
print 'cannot open', arg
else:
print arg, 'has', len(f.readlines()), 'lines'
f.close()
else bloğu kullanmak try bloğuna
ek satırlar eklemekten iyidir çünkü bu try ... except
ifadesi tarafından korunan kodun oluşturmadığı bir istisnanın kazara
yakalanmasını engeller.
Bir istisna meydana geldiğinde istisna bağımsız değişkeni olarak bilinen
bir değer de bulunabilir. Bağımsız değişkennin varlığı ve türü istisnanın
türüne bağlıdır. Bağımsız değişkeni olan istisna türleri için
except bloğunda istisna adından (ya da listesinden)
sonra bağımsız değişken değerini alacak bir değişken belirtilebilir:
>>>try:...spam()...except NameError, x:...print 'name', x, 'undefined'...name spam undefined
Bir istisnanın bağımsız değişkeni var ise ele alınmayan istisna mesajının son kısmında (`ayrıntı') basılır.
İstisna işleyiciler (exception handlers) sadece try
bloğu içinde meydana gelen istisnaları değil try
bloğundan çağırılan (dolaylı olarak bile olsa) işlevlerdeki istisnaları
da ele alır. Örnek:
>>>def this_fails():...x = 1/0...>>>try:...this_fails()...except ZeroDivisionError, detail:...print 'Handling run-time error:', detail...Handling run-time error: integer division or modulo
8.4. İstisna Oluşturma
raise deyimi yazılımcının kasıtlı olarak bir istisna
oluşturmasını sağlar. Örnek:
>>> raise NameError, 'Merhaba'
Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: Merhaba
raise için ilk bağımsız değişken oluşturulacak
istisnanın adıdır ve ikinci bağımsız değişken ise istisnanın bağımsız
değişkenidir.
Eğer bir istisnanın oluşup oluşmadığını öğrenmek istiyor; fakat bunu
ele almak istemiyorsanız, raise ifadesinin istisnayı
tekrar oluşturmanıza mkan veren daha basit bir biçimi var:
>>>try:...raise NameError, 'Merhaba'...except NameError:...print 'Bir istisna gelip geçti!'...raise...Bir istisna gelip geçti! Traceback (most recent call last): File "<stdin>", line 2, in ? NameError: Merhaba
8.5. Kullanıcı Tanımlı İstisnalar
Programlar yeni bir istisna sınıfı yaratarak kendi istisnalarını
isimlendirebilir. İstisnalar genellikle, doğrudan veya dolaylı olarak,
Exception sınıfından türetilir. Örnek:
>>>class bizimHata(Exception):...def __init__(self, deger):...self.deger = deger...def __str__(self):...return `self.deger`...>>>try:...raise bizimHata(2*2)...except bizimHata, e:...print 'İstisnamız oluştu, deger:', e.deger...İstisnamız oluştu, deger: 4>>>raise bizimHata, 'aaah!' Traceback (most recent call last): File "<stdin>", line 1, in ? __main__.bizimHata: 'aaah!'
İstisna sınıfları diğer sınıfların yapabildiği her şeyi yapabilecek şekilde tanımlanabilirler, fakat genellikle basit tutulurlar ve sıklıkla sadece istisnayı işleyenlerin hata hakkında bilgi almasını sağlayacak birkaç özellik sunar. Birkaç farklı istisna oluşturabilen bir modül yaratırken, yaygın bir uygulama da bu modül tarafından tanımlanan istisnalar için bir temel sınıf yaratıp ve farklı hata durumları için bundan başka istisna sınıfları türetmektir:
class Error(Exception):
"""Bu modüldeki istisnalar için temel sınıf."""
pass
class GirisHatasi(Error):
"""Giriş hataları için oluşacak istisna.
Özellikler:
ifade -- hatanın oluştuğu giriş ifadesi
mesaj -- explanation of the error
"""
def __init__(self, ifade, mesaj):
self.ifade = ifade
self.mesaj = mesaj
class GecisHatasi(Error):
"""İzin verilmeyen bir durum geçişine teşebbüs edildiğinde oluşacak istisna.
Özellikler:
onceki -- geçiş başlangıcındaki durum
sonraki -- istenen yeni durum
mesaj -- durum geçişine izin verilmemesinin sebebi
"""
def __init__(self, onceki, sonraki, mesaj):
self.onceki = onceki
self.sonraki = sonraki
self.mesaj = mesaj
Çoğu standart modül kendi tanımladıkları işlevlerde meydana gelen hataları rapor etmek için kendi istisnalarını tanımlar.
Sınıflar üzerine daha fazla bilgi sonraki bölümünde sunulacaktır.
8.6. Son İşlemlerin Belirlenmesi
try deyiminin her durumda yürütülecek işlemleri
belirten seçimlik bir bloğu da vardır. Örnek:
>>>try:...raise KeyboardInterrupt...finally:...print 'Goodbye, world!'...Goodbye, world! Traceback (most recent call last): File "<stdin>", line 2 KeyboardInterrupt
finally bloğu try bloğu içinde
bir istisna oluşsa da oluşmasa da yürütülür. Bir istisna oluşursa
finally bloğu icra edildikten sonra istisna tekrar
oluşturulur. Finally bloğu try
deyimi break veya return ile
sonlanırsa da icra edilir.
try deyiminin bir ya da daha fazla
except bloğu veya bir finally
bloğu olmalıdır; ancak her ikisi bir arada olamaz.