Python Programlama Dili

Bölüm 2

Genel Yöntemler

Top Navigasyon

Ders 16

2.4 - Hataların Yakalanması

Programcılık zor iştir. Yazılan programlarda mutlaka hatalar çıkar. Bunlardan bazıları program hatası, bazıları kullanıcıdan kaynaklanan yanlış ver girişleri, bazıları dosyaların okunmasında oluşabilen hatalı girişler, daha da akla gelmeyebilecek birçok hata kaynağı olabilir.

Programlarda hatalara bilgisayarcıların konuşma stilinde "istisnalar (exceptions)" adı verilir. Hata oluşumu, "istisna fırlatıldı (exception raised)" olarak kabul edilir.

Güierden birgün, Nasreddin Hocayı cübbesinin cebinde kocaman bir bıçakla yakalamışlar ve "Hocam ne bu?" diye sormuşlar. Hoca hazırcevap, "ben bununla kitapların hatalarını düzeltiyorum" diye yanıtlamış. Bu sefer,"aman hocam bu kadar büyük bıçak gerekli mi?" demişler. O, da "bazen öyle hatalar oluyor ki, bu bile küçük geliyor!" demiş.

Bilgisayar hatalarının sonuçları bazen ağır olur.  Çok bilinen bir bilim kurgu filmi olan "Brezilya" filminde, bir insanın yaşamında, küçük bir bilgisayar hatasının neden olduğu, inanılmaz kötü sonuçlar gösterilir.

Güncel, bir bilgisayar hatası olayı, Ariane5 füzesinin uzaya gönderilmesine yaşanmıştır. Ariane 5 roketinin uzaya gönderilmesi öncesinde, atış işlemlerini gerçekleştirilen bilgisayar programı, çok iyi çalışmış olam Ariane4 roketinin programı olarak alınmıştır. Oysa ki, Ariane4'ün iç yapısı 32 bit, Ariane5'in iç yapısı 64 bit üzerine kurulmuştu. Bunun çözümü için, küçük bir dönüşüm modülü yazıldı ve roket ateşlendi. Aksi gibi, o modülde bir arıza çıktı ve veriler bir üst modüle uygun olarak gönderilemedi. Modülde, hata yakalama prosedürü yeterli değildi. Bunun üzerine hata, katmanlar üzerinden ana parograma kadar geldi ve alarm durumu oluştu. Yedek bilgisayar devreye girdi, fakat on da aynı bilgiler vardı. Sonuçta rotorlar aşırı hızla dönmeye başladı ve roket infilak etti.

Ariane5 canlı taşımıyordu, o yüzden sonuçta en nihayet para ve vakit kaybı oldu. Ama bilgisayarlar, MR çekimleri, radyasyon tedavileri, Talyum taramaları, daha birçok insan yaşamı ile ilgili cihazlarda kullanılır. Burada hata cana zarar verecek sonuçlara neden olabilir. Bu yüzden bilgisayar programlarında hata yakalama prosedürleri önem taşır. Bir ticari bilgisayar programında, kodların yaklaşık yüzde ellisinin hata yakalama ile ilgili olabileceği düşünülür.

Bu seksiyonda, genel olarak hata yakalama ve yönetme yöntemleri üzerinde duracağız.

2.4.1 - Hataların Yönetimi

Bir bilgisayar programında, bir istisna fırlatıldığında, program derleyicisi (veya yorumlayıcısı) programı derhal bir hata mesajı görüntüleyerek durudurdur. Hata mesajlarının, pek kullanıcı dostu olmamalarının yanısıra, her istisna fırlatıldığında, programın durdurulması da gerekmez. Belki belirli düzeltmeler yapılarak, prograın sorunsuz olarak işlevini tamamlaması daha istenilen bir çalışma yöntemidir. En azından, daha kullnıcı dostu ve açıklayıcı hata mesajları oluşturulabilir. Bir log dosyasına oluşan istisna kaydı yapılabilir ve buna benzer birçok önlem alınabilir. Bütün bu yönlemlere, "İstisnaların Yönetimi" adı verilir

Küçük programlarda herşey kontrol altında gibi görülebilir. Bu nedenle, küçük programlarda istisnaların yönetilmesi yapılmayabilir. Bu nedenle, uygulamalarımızda istisna yönetimi gerkli görülmememiştir. Buna rağmen, özellikle kullnıcının veri girdiği veya dosyalarla veri alışverişi yapılan programlarda, program küçük de olsa, istisnaların yönetilmesi yararlı olur.

Python programlama dilinde, istisnaların yönetimi için aşağıdaki bildirimlerden yararlanılır.

try:
    <statements>
except [<exception_name> [, <instance_variable>]]:
<exception handling statements>
[else: <statements executed only when no exception is raised>] [finally: <statements always executed>]

Burada, try bloğu normal program adımlarını içerir. Eğer burada bir istisna fırlatılırsa, program kontrolü, except bloğuna geçer. Burada istisnanın oluşması ile alınması gereken önlemler alınmaya çalışılır. Eğer bu yöntemler yeterli olmazsa, mutlaka çalışacak olan finally bloğunda olaylar kontrol altına alınmaya çalışılır.

İstisna yönetiminin sağlanmasında, try/except dışında her bildirim terimin kullanımı isteğe bağlıdır. Ayrıca, except bloğunda istisna ismi ve istisnanaın atanacağı istisna sınıf değişkeninin belirtilmesi de isteğe bağlı olmasına karşın, bunların kullanılması yararlı olur.

Python 3.4.2 dokümantasyonunda, istisna sınıf hiyerarşisi, aşağıda görüldüğü gibi, belirtilmiştir.

The class hierarchy for built-in exceptions is:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

Bu istisna sınıflarının oluştukları koşulların geniş açıklamaları aynı kaynakta verilmiştir.

İlk olarak en basit bildirimlerin kullanıldığı bir istisna yönetim sekansını, aşağıdaki programda görüldüğü gibi açıklayalım.

#ders16program1.py

try:
a = input("Bir Sayı Giriniz = ");  

b = 12 / a; print (b); except: print("Bir İstisna Fırlatıldı !"); Sonuç:
Bir Sayı Giriniz :"qqq"
Bir İstisna Fırlatıldı !

Eğer konsoldan input ile bir veri girilirse bu bir sözel tipte veridir. Bu veri üzerinde aritmetik işlemler yapılması için, sayısal bir veri tipine dönüştürülmesi gereklidir. Eğer konsoldan girilen sözel veri, sayısal bir değere dönüştürülemeyen nitelikte ise, dönüştürme işlemi anında bir istisnanın fırlatılması kaçınılmazdır.

Yukarıdaki programda da beklenen olmuş ve istisnanın fırlatılması ile kontrol except içeriğine geçerek ilk bildirimi çalıştırmıştır.

Eğer programda bir hata oluşmazsa, try bloğundaki bildirimler tamamen çalıştırılır, tüm bildirimler tamamlanınca, kontrol (varsa) else bloğuna geçer. Aşağıdaki programda bu olay izlenebilir.

# ders16program2

try:
a = input("Bir Sayı Giriniz = ");

a = float(a);   

b = 12 / a; print (b); except: print("Bir İstisna Fırlatıldı !"); else: print("İşlem Başarı ile Gerçekleştirildi !"); Sonuç:
Bir Sayı Giriniz :65
0.18461538461538463
İşlem Başarı ile Gerçekleştirildi !

Bir istisna fırlatıldığında, programı except bloğunda düzelterek yoluna devam etmesi sağlanabilir. Bu konuda dikkatli olunması ve try bloğunda çalışmayan bir yöntemin, except bloğunda da çalışmayacağı hatırda tutulmalıdır. Hatanın giderilmesi amacını taşıyan except bloğunda uygulanacak yöntemin, try bloğunda uygulanan yönteme göre, hata verdirmeyecek farklı bir yöntem olması gerekir. Yoksa, try bloğundaki yöntemin aynısını burada uygulamak, try bloğunda oluşan hatanın, except bloğunda da oluşmasını sağlamaktan başka bir yararı olmaz. Bu gibi üstüste yapılan hatalar, programın yeterliği üzerinde kuşku yaratır. Aşağıdaki programda, try bloğunda uygulanan veri giriş yönteminin başarısızlığı üzerine, except bloğunda, bu hatanın bir öntanım ataması ile giderildiğini görüyoruz. Yani, aşağıdaki programda, try bloğunda uygulanan yöntem ile, except bloğunda uygulanan yöntem birbirlerinden farklıdır ve her zaman da öyle olmalıdır. Bu aşamada, hiç değilse programın aniden devreden çıkması önlenmiş oluyor. Bundan sonra, programcı olayı izlemeli ve programın devam etmesi uygun değilse,  kontrollü bir şekilde sonlandırılmasını sağlamalıdır.

    # ders16program3

kontrol = True;

try: a = input("Bir Sayı Giriniz = ");

b = 12 / a; print ('b = 'b); kontrol = False; except: print (kontrol); a = 0.1;

b = 12 / a; print ('b = 'b); print ('Veri Girişi Başarısız, a ' ya öntanım değeri verildi !'); kontrol = False; else: print (İşlem Başarı ile Gerçekleştirildi !); finally: if kontrol: a = 0.1;

b = 12 / a;

print ('b = 'b); print ('Veri Girişi Başarısız, a ' ya öntanım değeri verildi !'); Sonuç:
Bir Sayı Giriniz : 128
b = 0.09375
İşlem Başarı ile Gerçekleştirildi !
False

Yukarıdaki program, kullanıcının gireceği değere göre iki farklı yöne doğru gelişebilecek türde bir programdır. Kullanıcı konsoldan bir sayısal değer girerse, Python derleyicisi bunu sayıya çevrilebilecek bir sözel veri olarak algılar. Sayısal bir değere çevrilebilecek uygun bir bir sözel veriyi, sayısal bir değere dönüştürebilecek bir program adımı ile, konsoldan girilmiş olan, uygun bir sözel veri, sayısal bir türe dönüştürülür ve üzerinde her türlü sayısal işlem yapılabilir. Bu şekilde, try bloğunun tüm program adımları çalıştırılır, kontrol izleme değişkeninin değeri False değerine dönüştürülür ve program kontrolü else bloğuna girer, buradaki tüm program adımları çalıştırılır ve program kontrolü finally bloğuna girer.

Her try/except/finally sekansında, program kontrolünün mutlaka girmek zorunda olduğu finally bloğu, son kurtarma bloğudur. Yukarıdaki programda, finally bloğunda, önce izleme değişkeninin değeri incelenir. Bu değer hala True ise, herhangibir nedenden dolayı henüz a değişkenine bir değer atanamamış demektir. Burada bir acil düzeltme işlemi uygulanır ve a değişkenine bir öntanım değeri atanır. Ne except , ne de finally bloğunda, kullanıcının katkısının aranacağı hiçbir riskli işlem yapılmaz. Her şey önceden belirlenmiş bir kurtarma prosedürüne göre yürütülür. Burada kurtarma prosedürü, a değişkenine 0.1 değerinde bir öntanım değeri atamaktır. İşlem bitip program kontrolü finally bloğunu terkederken, artık a değişkenine bir değer atanması işlemi de mutlaka gerçekleşmiş olacaktır. Sorun, a değişkenine atanacak olan 0.1 ön tanım değerinin programın ileri aşamalarında bir sorun çıkarıp çıkarmayacağıdır. Sorun çıkabileceği saptanırsa, programı uygun bir şekilde sonlandırmak gerekir.

Konsoldan sayıya çevrilemeyecek bir sözel veri girilirse, try bloğunda işler yolunda gitmeyecek demektir. Konsoldan girilen değerin sayısal bir türe çevrilmesi program adımında bir istisna oluşur, program kontrolü derhal try bloğunu terkeder ve except bloğuna girer. Bu durumda, sözel veriyi sayısal veriye çevirecek program adımından sonraki hiçbir program adımı, çalıştırılamayacak demektir. Bu durumda kontrol değişkeninin değeri hala True olarak kalacaktır.

Program kontrolünün geçtiği except bloğunda, bir kurtarma prosedürü uygulanır. İşler yolunda giderse, a değişkenine 0.1 değerinde bir öntanım değeri atanmıış, b değişkenin değeri hesaplanmış ve kontrol değişkeninin değeri False olarak değişmiş olacaktır. Program kontrolü finally bloğuna geçer.

Finaly bloğunda, except bloğunda işlerin yolunda gidip gitmediği, kurtarma işleminin başarılı olup olmadığı kontrol edilir. Bunu göstergesi, kontrol değişkenin değeridir. Eğer kontrol değişkeninin değeri, hala True ise, bir nedenden dolayı kurtarma işlemi çalışmamış demektir. Bu durumda, kurtarma işlemi tekrarlanır. Aksi halde, yani kontrol değişkeninin değeri False ise, bu durumda except bloğunda işler yolunda gitmiş ve kurtarma işlemi gerçekleşmiş demektir. Bu şekilde, finally bloğunda hiçbir işleme gerek kalmamış demektir ve program kontrolu, try/except/finally bloğundan bir sonraki program adımına geçer. Uygunsuz bir verinin sonuçları aşağıda görülmektedir.

Sonuç:Bir Sayı Giriniz :"qqq"
True
a = 0.1
b = 120.0
Veri Girişi Başarısız, a ' ya öntanım değeri verildi !
False

Görüldüğü gibi, except bloğunda, kurtarma işlemi başarıya ulaşmış ve finally bloğunda yapacak bir şey kalmamış.

Bir except bildiriminde, hata türü bildirilmemişse, bu total bir except bildirimidir. Bu durumda, try bloğunda hangi tür bir hata oluşursa oluşsun, daima except bloğunda yakalanacaktır.

Total veya anonim (isimsiz) bir except bloğu sadece tek bir kurtarma işlemini organize edebilir. Eğer farklı istisna türleri için farklı kurtarma mekanizmalarının uygulanması söz konusu ise, o zaman her except bloğunun yakalayacağı istisna sınıfının belirtilmesi gerekir. Bir try bloğu için farklı istisna türlerini yakalayabilen birden fazla except <istisna sınıfı> bloğu olabilir.

Yukarıdaki listede, Python programlama dilinin tüm istisna türleri verilmiştir. Fakat doğal olarak, programcının her oluşabilecek hata türünü kesin olarak listeden seçebilmesi olası değildir. Bunun için, bazı boş deneyler yapmak, oluşabilecek istisna türlerini deneysel olarak algılamak ve kurtarma mekanizmalarını bu gözlemlere göre ayarlamak en doğrusu olacaktır.

Aşağıdaki programda bir boş deneme yaparak hatanın türünü saptıyoruz.

# ders16program4

a = input("Bir Sayı Giriniz = ");
      
a = float(a);   

b = 12 / a; print ('b = ' , b); Sonuç:
Traceback (most recent call last):
File "C:\abyss web server\htdocs\sites\bedriemir\Python\uygulamalar\ders16\ders16program4.py", line 4, in a = float(a) ValueError: could not convert string to float: '"qqq"'

Fırlatılan istisnanın sınıfını saptadık : ValueError. Bu durumda, programımızın except kodunu spesifik olarak sadece ValueError istisna sınıfındaki istisnaları yakalayacak şekilde belirtebiliriz. Aşağıdaki programda bu değişiklik yapılmıştır.

    # ders16program5

kontrol = True

try:
a = input("Bir Sayı Giriniz")
    
a = float(a)    

b = 12 / a

print ('b = ', b) kontrol = False except ValueError: a = 0.1

print ('a = ', a) b = 12 / a

print ('b = ', b) print ("Veri Girişi Başarısız, a ' ya öntanım değeri verildi !") kontrol = False else: print ("İşlem Başarı ile Gerçekleştirildi !") finally: if kontrol: a = 0.1

print ('a = ', a) b = 12 / a

print ('b = ', b) print ('Veri Girişi Başarısız, a ' ya öntanım değeri verildi !') Sonuç:
Bir Sayı Giriniz = "zzz"
a = 0.1
b = 120.0
Veri Girişi Başarısız, a ' ya öntanım değeri verildi !

Yukarıdaki en son durumu ile programımız sadece ValueError hatasına karşı korumalı, diğer hata türlerine karşı korumasız durumdadır. Buna rağmen, programın bu halinde, başka hata olasılığı görünmüyor. Yine de, uzun vadede gözlemleri sürdürmeli ve çalışma sırasında başka istisnalar fırlatılırsa, bunlara karşı da savunma mekanizmalarını yeni except <istisna sınıfı adı> blokları ile geliştirmeliyiz.

Bir except <istisna sınıfı adı> bildiriminde, koruma sağlanan istisna sınıfının, bir sınıf değişkeninin adını bildirim sırasında belirterek bu sınıf değişkeninin oluşmasını otomatik olarak sağlayabilir ve bu şekilde oluşan istisna türü hakkında açıklayıcı bilgi alabiliriz. Aşağıdaki programda bu yöntem uygulanmaktadır.

# ders16program6.py

import sys try: a = input("Bir Sayı Giriniz = ") a = float(a) b = 12 / a

print('b = ', b) except ValueError as e: print("Hata Nedeni : ", e) print("Hata Sınıfı :", sys.exc_info()[0]) Bir Sayı Giriniz = "zzz" Hata Nedeni : could not convert string to float: '"zzz"' Hata Sınıfı : <class 'ValueError'>

Görüldüğü gibi, yukarıdaki yöntem, hataların belirlenmesi için önemli bilgilerin toplanmasına olanak sağlamaktadır.

2.4.2 - Hataların Oluşturulması

İstisnalar, gerektiğinde kullanıcılar tarafında da yaratılabilir. İstisnalar, öntanımlı bir sınıf ailesinin alt sınıfları olaraka organize edilmişlerdir. Kullanıcılar isterlerse mevcut öntanımlı sınıflardan birisinin türünde hataları oluşturabildikleri gibi, kendi hata sınıflarını da tanımlayarak hata oluşturabilirler. Bu konulara daha çok geniş çapta programlar için gerekli olabilecekleri için burada küçük örneklerle yetineceğiz. Bu konuda, Python dokümentasyonunda geniş bilgi bulunmaktadır.

Aşağıdaki uygulamada, mevcut hata sınıflarından birinin kullanıcı tarafından oluşturulması açıklanmaktadır.

# ders16program7.py

raise NameError('Bir NameError istisnası fırlatıldı')
      
******Program Sonucu************

Traceback (most recent call last):
File "C:\Abyss Web Server\htdocs\sites\bedriemir\Python\uygulamalar\ders16\ders16program7.py", line 8, in raise NameError('Bir NameError istisnası fırlatıldı') NameError: Bir NameError istisnası fırlatıldı

Görüldüğü gibi çok kolay olay olan hata oluşturulması için raise bildirimi uygulanmaktadır.

Kullanıcılar isterlerse yeni hata sınıflarıda tanımlayabilirler. Bunun için bir hata sınıfı tanımlanır ve bu hata sınıfının bir sınıf örneği oluşturulur. Bu yöntem, aşağıdaki programda uygulanmıştır.

# ders16program8.py

classBenimHatam(Exception):
def__init__(self,değer ):
self.değer = değer

def__str__(self): returnrepr(self.değer) try: raiseBenimHatam('Benim Hatam, Özür Dilerim') except BenimHatam as hata: print(hata) ******Program Sonucu************
'Benim Hatam, Özür Dilerim'

Python programlama dilinin kullanılması üzerine yaptığımız kısa tanıtımın ikinci bölümü olan "Genl Yöntemler" bölümü burada tamamlanmış oluyor. Bundan sonra, üçüncü ve son bölüm olan "Veri Tipleri" bölümü ile devam ediyoruz.