Skip to content

5. İstisnalar

Şimdi istisnalara geçelim.

Image

5.1. [exceptions_01] komut dosyası

İlk komut dosyası, istisnaları yönetmenin gerekliliğini göstermektedir.

# kasıtlı olarak bir hata oluşturulur
x = 4 / 0

Ne olacağını görmek için kasıtlı olarak bir hata oluşturuyoruz (exceptions-01.py):

1
2
3
4
5
6
7
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/exceptions/exceptions_01.py
Traceback (most recent call last):
  File "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/exceptions/exceptions_01.py", line 2, in <module>
    x = 4 / 0
ZeroDivisionError: division by zero

Process finished with exit code 1
  1. satır bize şunu gösterir:

  2. istisnanın türü: ZeroDivisionError;

  3. ilgili hata mesajı: sıfıra bölme. Mesaj İngilizce. Bu, değiştirmek isteyebileceğimiz bir şeydir.

Temel bir kural, Python yorumlayıcısı tarafından üretilen istisnaları önlemek için her şeyi yapmamız gerektiğidir. Hataları kendimiz yönetmeliyiz.

İstisna işleme sözdizimi şöyledir:


try:
    actions susceptibles de lancer une exception
except (Ex1, Ex2…) as ex:
    actions de gestion de l'exception [ex]
except (Ex11, Ex12…) as ex:
    actions de gestion de l'exception [ex]
finally:
   actions toujours exécutées qu'il y ait exception ou non

try bloğunda, bir istisna (hata) oluştuğu anda işlemlerin yürütülmesi durur. Bu durumda, yürütme, except cümlelerinden birindeki işlemlerle devam eder:

    1. satır: Oluşan [ex] istisnası, (Ex1, Ex2…) tuple'ına ait bir türdeyse veya bunlardan birinden türetilmişse, 4. satırdaki işlemler yürütülür;
    1. satır: istisna 3. satır tarafından yakalanmamışsa ve başka bir [except] cümlesi varsa, aynı süreç tekrarlanır. Vb…;
  • [try]'te ortaya çıkabilecek farklı istisna türlerini yönetmek için gerekli olduğu kadar çok sayıda [except] maddesi olabilir;
  • istisna hiçbir [except] maddesinde işlenmemişse, çağrı yapan koda iletilir. Çağrı yapan kodun kendisi bir try / except yapısı içindeyse, istisna yeniden işlenir; aksi takdirde çağrılan yöntemler zinciri boyunca yukarı doğru iletilmeye devam eder. Son çare olarak, istisna Python yorumlayıcısına ulaşır. Yorumlayıcı, bu durumda yürütülen programı durdurur ve önceki örnekte gösterilen türde bir hata mesajı görüntüler. Dolayısıyla kural şudur: ana program, çağrılan yöntemlerden yukarı doğru iletilebilecek tüm istisnaları durdurmalıdır;
    1. satır: [finally] ifadesi, istisna oluşmuş olsun (except bloğunun devamı) ya da oluşmasın (try bloğunun devamı), her durumda yürütülür. Bu durum, bir istisna oluşmuş ve yakalanmamış olsa bile geçerlidir. Bu durumda, istisna çağıran koda ulaşmadan önce [finally] cümlesi yürütülecektir;

Bir istisna, meydana gelen hatayla ilgili bilgileri de beraberinde taşır. Bu bilgilere aşağıdaki sözdizimiyle ulaşılabilir:


except MyException as exception:

[exception], meydana gelen istisnadır. [exception.args] ise istisnanın parametre tuple'ını temsil eder.

Bir istisna oluşturmak için şu sözdizimi kullanılır:


raise MyException(param1, param2…)

kullanılır; burada MyException genellikle BaseException sınıfından türetilmiş bir sınıftır. Sınıfın oluşturucusuna aktarılan parametreler, [ex]'in [except] cümlesi tarafından yakalanan istisna olması durumunda, istisna yakalama yapılarının except cümlesinde [ex.args] sözdizimi ile kullanılabilir olacaktır.

Bu kavramlar aşağıdaki komut dosyasında gösterilmiştir.

5.2. [exceptions_02] betiği

Aşağıdaki komut dosyası hataları açıkça yönetir:


# istisna yönetimi
essai = 0

# bir hata oluşturulur ve yönetilir
x = 2
try:
    x = 4 / 0
except ZeroDivisionError as erreur:
    # hata, yakalanan istisnadır
    print(f"essai n° {essai} : {erreur}")
# x'in değeri değişmedi
print(f"x={x}")

# yeniden başlıyoruz
essai += 1
try:
    x = 4 / 0
except BaseException as erreur:
    # en genel istisna yakalanıyor
    # hata, yakalanan istisnadır
    print(f"essai n° {essai} : {erreur}")

# farklı türde istisnalar yakalanabilir
# yürütme, istisnayı işleyebilen ilk [except]'te durur
essai += 1
try:
    x = 4 / 0
except ValueError as erreur:
    # bu istisna burada meydana gelmez
    print(f"essai n° {essai} : {erreur}")
except BaseException as erreur:
    # en genel istisna yakalanır
    print(f"essai n° {essai} : (Exception) {erreur}")
except ZeroDivisionError as erreur:
    # belirli bir tür yakalanıyor
    print(f"essai n° {essai} : (ZeroDivisionError) {erreur}")

# sırayı değiştirerek yeniden başlıyoruz 
essai += 1
try:
    x = 4 / 0
except ValueError as erreur:
    # bu istisna burada gerçekleşmiyor
    print(f"essai n° {essai} : {erreur}")
except ZeroDivisionError as erreur:
    # belirli bir tür yakalanıyor
    print(f"essai n° {essai} : (ZeroDivisionError) {erreur}")
except BaseException as erreur:
    # en genel istisna yakalanıyor
    print(f"essai n° {essai} : (Exception) {erreur}")

# argüman içermeyen bir except ifadesi
essai += 1
try:
    x = 4 / 0
except:
    # istisnanın niteliği önemli değildir
    print(f"essai n° {essai} : il y a eu un problème")

# başka bir istisna türü
essai += 1
try:
    # x tamsayıya dönüştürülemez
    x = int("x")
except ValueError as erreur:
    # hata, yakalanan istisnadır
    print(f"essai n° {essai} : {erreur}")

# bir istisna, programa erişilebilir bir tuple içinde bilgi taşır
essai += 1
try:
    x = int("x")
except ValueError as erreur:
    # hata, yakalanan istisnadır
    print(f"essai n° {essai} : {erreur}, paramètres={erreur.args}")

# istisnalar atılabilir
essai += 1
try:
    raise ValueError("param1", "param2", "param3")
except ValueError as erreur:
    # hata, yakalanan istisnadır
    print(f"essai n° {essai} : {erreur}, paramètres={erreur.args}")


# kendi istisnalarınızı oluşturabilirsiniz
# bunlar [BaseException] sınıfından türetilmelidir
class MyError(BaseException):
    pass


# MyError istisnası tetiklenir
essai += 1
try:
    raise MyError("info1", "info2", "info3")
except MyError as erreur:
    # hata, yakalanan istisnadır
    print(f"essai n° {essai} : {erreur}, paramètres={erreur.args}")

# MyError istisnası bir hata mesajıyla atılır
essai += 1
try:
    raise MyError("mon msg d'erreur")
except MyError as erreur:
    # hata, yakalanan istisnadır
    print(f"essai n° {essai} : {erreur.args[0]}")

# finally bloğu her zaman yürütülür
# istisna olsun ya da olmasın
essai += 1
x = None
try:
    x = 1
except:
    # istisna
    print(f"essai n° {essai} : exception")
finally:
    # her durumda yürütülür
    print(f"essai n° {essai} : finally x={x}")

essai += 1
x = None
try:
    x = 2 / 0
except:
    # istisna
    print(f"essai n° {essai} : exception")
finally:
    # her durumda yürütülür
    print(f"essai n° {essai} : finally x={x}")

# bir koşul eklemek zorunlu değildir [except]
essai += 1
try:
    # hata verilir
    x = 4 / 0
finally:
    # her durumda yürütülür
    print(f"essai n° {essai} : finally x={x}")

Notlar:

  • 4-12. satırlar: sıfıra bölme hatası işlenir;
    1. satır: Oluşan istisnayı yakalayıp ekrana yazdırır;
    1. satır: Meydana gelen istisna nedeniyle, x, 7. satırda bir değer almadı ve dolayısıyla değeri değişmedi;
  • satır 14-21: Aynı işlemi tekrarlıyoruz, ancak bu sefer BaseException türünde daha üst düzey bir istisnayı yakalıyoruz. ZeroDivisionError istisnası, BaseException sınıfından türediği için, except cümlesi bunu durduracaktır;
  • 23-36. satırlar: Çeşitli istisna türlerini yönetmek için birden fazla except cümlesi eklenir. İstisna hiçbir except cümlesini karşılamıyorsa, tek bir except cümlesi yürütülecek ya da hiçbiri yürütülmeyecektir;
  • satır 38-50: [except] maddelerinin sırasını değiştirerek aynı işlemi tekrarlarız; böylece bu maddenin rolünü gösteririz;
  • satır 52-58: except cümlesi hiçbir argüman içermeyebilir. Bu durumda, tüm istisnaları durdurur;
  • satır 60-67: ValueError istisnasını ekler;
  • satır 69-75: istisna tarafından taşınan bilgiler alınır;
  • satır 77-83: bir istisnayı tetikleme (raise) yöntemi tanıtılır;
  • satır 86-106: özel bir istisna sınıfı olan MyError'in kullanımını gösterir. MyError sınıfı, yalnızca temel sınıf olan BaseException'ten türetilmiştir. Temel sınıfa hiçbir şey eklemez. Ancak artık except cümlelerinde açıkça adlandırılabilir;
  • 108-130. satırlar: finally cümlesinin kullanımını gösterir;
  • 132-139. satırlar: Bu satırlar, [except] cümlesinin zorunlu olmadığını göstermektedir;

Ekran çıktısı şu şekildedir:

C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/exceptions/exceptions_02.py
Traceback (most recent call last):
  File "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/exceptions/exceptions_02.py", line 136, in <module>
    x = 4 / 0
ZeroDivisionError: division by zero
essai n° 0 : division by zero
x=2
essai n° 1 : division by zero
essai n° 2 : (Exception) division by zero
essai n° 3 : (ZeroDivisionError) division by zero
essai n° 4 : il y a eu un problème
essai n° 5 : invalid literal for int() with base 10: 'x'
essai n° 6 : invalid literal for int() with base 10: 'x', paramètres=("invalid literal for int() with base 10: 'x'",)
essai n° 7 : ('param1', 'param2', 'param3'), paramètres=('param1', 'param2', 'param3')
essai n° 8 : ('info1', 'info2', 'info3'), paramètres=('info1', 'info2', 'info3')
essai n° 9 : mon msg d'erreur
essai n° 10 : finally x=1
essai n° 11 : exception
essai n° 11 : finally x=None
essai n° 12 : finally x=None

Process finished with exit code 1

5.3. [exceptions_03] komut dosyası

Bu yeni komut dosyası, istisnaların çağrı zincirinde yukarı doğru iletilmesini göstermektedir:


# özel bir istisna
class MyError(BaseException):
    pass


# üç işlev
def f1(x: int) -> int:
    # istisnalar yönetilmez - otomatik olarak üst katmana iletilir
    return f2(x)


def f2(y: int) -> int:
    # istisnalar yönetilmiyor - otomatik olarak üst katmana iletiliyor
    return f3(y)


def f3(z: int) -> int:
    if (z % 2) == 0:
        # z çift ise, bir istisna atılır
        raise MyError("exception dans f3")
    else:
        return 2 * z


# ---------- main

# istisnalar, çağrılan yöntemler zinciri boyunca yukarı doğru iletilir
# bir yöntem tarafından yakalanana kadar. Burada bu yöntem main olacaktır
try:
    print(f1(4))
except MyError as erreur:
    print(f"type : {type(erreur)}, arguments : {erreur.args}")


# istisnaları zenginleştiren üç başka işlev daha vardır
def f4(x: int) -> int:
    try:
        return f5(x)
    except MyError as erreur:
        # istisna zenginleştirilir ve ardından yeniden atılır
        raise MyError("exception dans f4", erreur)


def f5(y: int) -> int:
    try:
        return f6(y)
    except MyError as erreur:
        # istisnayı zenginleştirip yeniden atıyoruz
        raise MyError("exception dans f5", erreur)


def f6(z: int) -> int:
    if (z % 2) == 0:
        # z çift ise bir istisna atıyoruz
        raise MyError("exception dans f6")
    else:
        return 2 * z


# ---------- ana işlev
try:
    print(f4(4))
except MyError as erreur:
    # istisna görüntülenir
    print(f"type : {type(erreur)}, arguments : {erreur.args}")
    # istisna yığınını geriye doğru izleyebiliriz
    err = erreur
    # hata mesajı görüntülenir
    print(err.args[0])
    # Bir istisna kapsüllenmiş mi?
    while len(err.args) == 2 and isinstance(err.args[1], BaseException):
        # istisna değişikliği
        err = err.args[1]
        # 1. argüman hata mesajıdır
        print(err.args[0])

Notlar:

  • 25-32. satırlarda, main --> f1 --> f2 --> f3 (30. satır) çağrısında, f3 tarafından tetiklenen MyError istisnası, main'e kadar iletilecektir. Bu istisna, 31. satırdaki except maddesi tarafından işlenecektir;
  • 61-75. satırlar: main --> f4 --> f5 --> f6 çağrısında (62. satır), f6 tarafından tetiklenen MyError istisnası, main'e kadar geriye doğru izlenecektir. Ardından, 63. satırdaki except maddesi tarafından işlenecektir. Bu sefer, çağıran işlevler zincirinde yukarı doğru ilerlerken, yukarı doğru ilerleyen MyError istisnası, kendisi de başka bir istisna içinde kapsüllenmiştir;
  • 66-75. satırlar: istisna yığınından nasıl yukarı çıkılacağını gösterir;
    1. satır: [isinstance(instance, Classe)] işlevi, [instance] nesnesinin [Classe] türünde veya bunun türevlerinden biri olması durumunda True değerini döndürür. Burada, en üst düzeydeki [BaseException] istisnasını kullandık; bu sayede tüm istisnaları yakalayabiliyoruz;

Ekran çıktısı şu şekildedir:

1
2
3
4
5
6
7
8
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/exceptions/exceptions_03.py
type : <class '__main__.MyError'>, arguments : ('exception dans f3',)
type : <class '__main__.MyError'>, arguments : ('exception dans f4', MyError('exception dans f5', MyError('exception dans f6')))
exception dans f4
exception dans f5
exception dans f6

Process finished with exit code 0