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
try
bloğu (try
veexcept
arasındaki ifade(ler)) işletilir. -
Hiçbir istisna oluşmaz ise
except
bloğu atlanır vetry
deyimin icrası son bulur. -
Eğer
try
bloğu içinde bir istisna oluşur ise bloğun geri kalanı atlanır. İstisnanın türüexcept
anahtar kelimesinden sonra kullanılan ile aynı isetry
bloğunun kalan kısmı atlanır veexcept
bloğu yürütülür. Programın akışıtry ... except
kısmından sonra gelen ilk satırdan devam eder. -
Adı
except
bloğunda geçmeyen bir istisna oluşur ise üst seviyedekitry
ifadelerine 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.