İstisnalar, modüller ve sistematik hata ayıklama

Bir programın doğru sözdizimine sahip olması, her durumda sorunsuz çalışacağı anlamına gelmez. Dosya bulunamayabilir, metin sayıya dönüştürülemeyebilir, beklenen anahtar sözlükte olmayabilir veya bir hesap sıfıra bölme yapabilir.

Bu hafta istisna (exception) kavramını, try/except ile belirli çalışma zamanı sorunlarını ele almayı, standart kütüphane modüllerini kullanmayı ve hata ayıklamayı daha sistematik bir sürece dönüştürmeyi öğreneceğiz.

Bu hafta neleri yapabilmelisiniz?

Bölümün sonunda:

  • söz dizimi hatası ile çalışma zamanı hatasını ayırt edebilmeli,
  • istisnanın ne olduğunu açıklayabilmeli,
  • try/except ile belirli bir hata türünü yakalayabilmeli,
  • gereğinden geniş except kullanımının neden sorunlu olduğunu açıklayabilmeli,
  • bir traceback’in en azından hata türünü ve ilgili satırı bulmak için nasıl okunacağını açıklayabilmeli,
  • standart kütüphaneden bir modülü import ile kullanabilmeli,
  • kendi modülünüzü oluşturma fikrini açıklayabilmeli,
  • main guard yapısının temel amacını açıklayabilmeli,
  • bir hatayı yeniden üretme, küçültme, tahmin kurma ve doğrulama adımlarıyla sistematik biçimde inceleyebilmelisiniz.

Hata türlerini ayırmak

Başlangıç düzeyinde üç hata sınıfını ayırmak yararlıdır.

Söz dizimi hatası

Python kodun yapısını okuyamaz:

if puan >= 60
    print("Geçti")

İki nokta eksiktir ve SyntaxError oluşur.

Çalışma zamanı hatası

Kod sözdizimsel olarak geçerlidir; ancak çalışırken sorun çıkar:

sayi = int("abc")

Burada ValueError oluşur.

Başka örnekler:

10 / 0

ZeroDivisionError

liste = [1, 2]
liste[5]

IndexError

Mantık hatası

Program çalışır, fakat yanlış sonuç üretir:

def daire_cevresi(yaricap):
    return 3.14 * yaricap

Program hata vermeyebilir; ancak çevre formülü eksiktir. Bu tür sorunları Python otomatik olarak her zaman fark edemez.

Important

Hata ayıklamada ilk soru “program çalışıyor mu?” değil, “sorun hangi sınıfa ait?” olabilir. Söz dizimi, çalışma zamanı ve mantık hataları farklı inceleme yolları gerektirir.

Söz dizimi, çalışma zamanı ve mantık hatalarının örnekleri ve birbirinden farkları.

İstisna nedir?

Python çalışırken olağan akışı sürdüremediği bir durumla karşılaştığında istisna oluşturabilir.

Örneğin:

int("on")

Python "on" metnini tam sayıya çeviremez ve ValueError üretir.

İstisna yalnızca “program bozuldu” bilgisi değildir. Hata türü, sorunun niteliği hakkında ipucu verir.

try / except

Belirli bir işlemin hata üretme ihtimali varsa ve bu durumu anlamlı biçimde ele alabiliyorsak try/except kullanabiliriz:

metin = "42"

try:
    sayi = int(metin)
    print("Sayı:", sayi)
except ValueError:
    print("Geçerli bir tam sayı değil")

metin = "abc" olduğunda dönüşüm başarısız olur ve except ValueError bloğu çalışır.

Başarılı ve başarısız sayı dönüşümlerinde try ve except ValueError akışının nasıl ilerlediğini gösteren hata yönetimi şeması.

Tahmin et — hangi blok çalışır?

Önce "75" ile sonucu tahmin edin. Sonra girdi_metni değerini "yetmis" yapıp tekrar çalıştırın.

Tarayıcı hücresinde input() kullanmıyoruz; kullanıcıdan gelmiş olabilecek metni değişkenle simüle ediyoruz.

Alıştırma — dönüşüm hatasını ele alın

Aşağıdaki kod "abc" değerinde hata veriyor:

Kodu try/except ValueError kullanacak biçimde değiştirin. Geçersiz metinde:

Geçerli sayı değil

yazdırılsın.

try: içinde dönüşümü yapın; except ValueError: içinde hata mesajını yazdırın.

girdi_metni = "abc"

try:
    sayi = int(girdi_metni)
    print(sayi)
except ValueError:
    print("Geçerli sayı değil")

Belirli hata türünü yakalamak

Şu kullanım teknik olarak birçok hatayı yakalayabilir:

try:
    ...
except:
    ...

Fakat hangi sorunun oluştuğunu gizleyebilir.

Örneğin amaç yalnızca sayı dönüşüm hatasını yönetmekse:

try:
    sayi = int(metin)
except ValueError:
    ...

daha açıklayıcıdır.

Warning

“Program hiçbir zaman hata vermesin” diye her şeyi geniş bir except ile kapatmak hata ayıklamayı zorlaştırabilir. Yalnızca anlamlı biçimde ele alabildiğiniz ve beklediğiniz hata türlerini yakalayın.

Birden fazla hata türü

Aşağıdaki fonksiyon metni sayıya dönüştürüp bölme yapıyor:

def bol(metin, bolen):
    sayi = int(metin)
    return sayi / bolen

İki farklı çalışma zamanı sorunu olabilir:

  • metin sayısal değilse ValueError,
  • bolen 0 ise ZeroDivisionError.

Bunları ayrı ele alabiliriz:

try:
    sonuc = bol("20", 0)
    print(sonuc)
except ValueError:
    print("Metin sayıya dönüştürülemedi")
except ZeroDivisionError:
    print("Sıfıra bölme yapılamaz")

Ayrı mesajlar sorunun nedenini görünür tutar.

İstisna mı, koşul mu?

Her kontrol için istisna gerekmez.

Örneğin puanın 0 ile 100 arasında olması normal bir iş kuralıdır:

if 0 <= puan <= 100:
    ...
else:
    ...

Metnin int() ile dönüştürülememesi ise dönüşüm sırasında oluşan çalışma zamanı problemidir:

try:
    puan = int(girdi_metni)
except ValueError:
    ...

Bu ayrım şu soruyla düşünülebilir:

Bu durum normal karar mantığının bir parçası mı?
Yoksa yapılan işlem beklenen türde veriyi işleyemedi mi?

Traceback okumak

Bir istisna yakalanmazsa Python hata iz dökümü (traceback) gösterir.

Örnek:

Traceback (most recent call last):
  File "rapor.py", line 12, in <module>
    toplam = fiyat_hesapla("abc")
  File "rapor.py", line 5, in fiyat_hesapla
    fiyat = float(metin)
ValueError: could not convert string to float: 'abc'

Başlangıçta traceback’i şu sırayla okuyabilirsiniz:

  1. En alttaki hata türünü bulun: ValueError.
  2. Mesajı okuyun: hangi değer dönüştürülemedi?
  3. Kendi kodunuzdaki ilgili satırı bulun.
  4. O satıra hangi değerlerin geldiğini kontrol edin.
  5. Sorunun kaynağını daha küçük örnekle yeniden üretin.
Tip

Traceback’teki en uzun veya en teknik satırdan başlamanız gerekmez. Hata türü ve kendi kodunuzdaki son ilgili satır çoğu zaman iyi başlangıç noktalarıdır.

Sistematik hata ayıklama

Rastgele satır değiştirmek yerine küçük bir süreç kullanalım.

Sorunu yeniden üret

Hata hangi girdiyle oluşuyor?

"abc" girildiğinde ValueError oluşuyor.

Beklenen davranışı yaz

Geçersiz metinde kullanıcıya açıklayıcı mesaj verilmeli.

Problemi küçült

Büyük program yerine yalnızca sorunlu dönüşümü deneyin:

int("abc")

Değerleri gözlemle

Gerekirse geçici çıktı ekleyin:

print("Dönüştürülecek değer:", girdi_metni)

Hipotez kur ve tek değişiklik yap

Sorun int() dönüşümünden kaynaklanıyor.

Yeniden doğrula

Hem hatalı hem geçerli örneği deneyin.

Bu süreç hata ayıklamayı “tahmin ederek kod değiştirme” işinden ölçülebilir bir incelemeye dönüştürür.

Modül nedir?

Python kodunu başka dosyalara ve yeniden kullanılabilir bileşenlere ayırmak için modül kavramı kullanılır.

Standart kütüphaneden örnek:

import math

print(math.sqrt(81))
print(math.pi)

math Python ile birlikte gelen standart kütüphane modüllerinden biridir.

Başka örnek:

from datetime import date

bugun = date.today()
print(bugun)

Bu hafta modül seçeneklerini ezberlemiyoruz. Ana fikir:

İhtiyacımız olan bazı işlevler Python’ın standart kütüphanesinde hazır modüller olarak bulunur.

Tahmin et ve çalıştır — math

İkinci çıktı, sonucu iki ondalık basamağa yuvarlayarak daha okunur hâle getirir.

Kendi modülümüz

Aynı dizinde hesaplar.py dosyamız olduğunu düşünelim:

def kdv_hesapla(tutar, oran):
    return tutar * oran

def indirimli_fiyat(fiyat, oran):
    return fiyat - fiyat * oran

Başka bir dosyada:

import hesaplar

print(hesaplar.kdv_hesapla(500, 0.20))

veya:

from hesaplar import kdv_hesapla

print(kdv_hesapla(500, 0.20))

Bu yapı büyük programlarda fonksiyonları anlamlı dosyalara ayırmamıza yardım eder.

Main guard

Bir Python dosyası hem doğrudan çalıştırılabilir hem de başka dosyadan modül olarak içe aktarılabilir.

Sık kullanılan kalıp:

def main():
    print("Program başladı")

if __name__ == "__main__":
    main()

Bu yapıya main guard denir.

Temel fikir:

  • dosya doğrudan çalıştırılırsa main() çağrılır,
  • dosya modül olarak içe aktarılırsa bu çağrı otomatik çalışmaz.

Şimdilik __name__ mekanizmasının bütün ayrıntılarını bilmemiz gerekmiyor.

Note

Main guard, Python anahtar sözcüğü değildir; __name__ özel değişkeni üzerinden kurulan yaygın bir Python kalıbıdır.

Alıştırma — modül fonksiyonunu kullanın

math modülünü kullanarak bir dik üçgende iki dik kenardan hipotenüsü hesaplayan fonksiyonu tamamlayın.

Formül:

√(a² + b²)

Beklenen sonuç 5.0dır.

math.sqrt(a ** 2 + b ** 2) ifadesini döndürebilirsiniz.

import math

def hipotenus(a, b):
    return math.sqrt(a ** 2 + b ** 2)

print(hipotenus(3, 4))

Hata ayıklamada fonksiyon ve assert

Bir mantık hatasını küçük fonksiyon testleriyle yakalayabiliriz:

def indirimli_fiyat(fiyat, oran):
    return fiyat * oran

assert indirimli_fiyat(100, 0.20) == 80

Bu assert başarısız olur. Fonksiyonun “indirimli fiyat” yerine “indirim miktarı” döndürdüğünü fark ederiz.

Doğrusu:

def indirimli_fiyat(fiyat, oran):
    return fiyat - fiyat * oran

Bu örnek önemli bir noktayı gösterir: istisna yoktur; hata beklenen davranış ile gerçek sonuç arasındaki farktan bulunur.

Üret — güvenli puan dönüştürme

puani_coz(metin) fonksiyonu yazın.

Kurallar:

  • Metin tam sayıya dönüştürülemiyorsa None döndürsün.
  • Sayı 0 ile 100 arasındaysa sayıyı döndürsün.
  • Sayı aralık dışındaysa None döndürsün.
  • Fonksiyon içinde print() kullanılmasın.

Başlangıç:

Beklenen:

85
None
None

try içinde puan = int(metin) yapın. except ValueError: durumunda return None; dönüşüm başarılıysa aralık kontrolü yapın.

def puani_coz(metin):
    try:
        puan = int(metin)
    except ValueError:
        return None

    if 0 <= puan <= 100:
        return puan

    return None

print(puani_coz("85"))
print(puani_coz("abc"))
print(puani_coz("120"))

Hata ayıklama kontrol listesi

Bir programda sorun olduğunda:

  • Hata türü nedir: söz dizimi, çalışma zamanı, mantık?
  • Sorunu aynı girdiyle yeniden üretebiliyor muyum?
  • Beklenen sonuç nedir?
  • Sorunu daha küçük bir örneğe indirebilir miyim?
  • Traceback hangi hata türünü ve hangi satırı gösteriyor?
  • O satırdaki değişkenlerin değerleri nedir?
  • Tek bir hipotezi sınayacak küçük değişiklik yapabilir miyim?
  • Düzeltme sonrası hem sorunlu hem normal örneği yeniden denedim mi?

Bu süreç, 14. haftadaki bütünleştirici uygulamada özellikle önemli olacaktır.

Bölüm özeti

Bu hafta programlarımızın hata durumlarını daha bilinçli ele almaya başladık.

  • Söz dizimi hatası kodun yapısında, çalışma zamanı hatası yürütme sırasında, mantık hatası ise sonuç davranışında ortaya çıkar.
  • İstisna, Python’ın yürütme sırasında olağan akışı sürdüremediği durumu temsil eder.
  • try/except, beklenen belirli istisnaları anlamlı biçimde ele almaya yarar.
  • Geniş except kullanımı gerçek hataları gizleyebilir.
  • Traceback’te hata türü ve kendi kodunuzdaki ilgili satır iyi başlangıç noktalarıdır.
  • Hata ayıklama yeniden üretme, küçültme, gözlemleme, hipotez kurma ve doğrulama süreci olarak ele alınabilir.
  • Standart kütüphane modülleri import ile kullanılabilir.
  • Kendi Python dosyalarımız da modül olabilir.
  • Main guard, dosyanın doğrudan çalıştırılması ile modül olarak içe aktarılması davranışlarını ayırmaya yardım eder.

Kendinizi kontrol edin

  1. SyntaxError ile ValueError arasındaki temel fark nedir?
  2. except ValueError: neden tek başına except: kullanımından daha açıklayıcıdır?
  3. Puanın 0–100 aralığında olması neden çoğunlukla koşulla kontrol edilir?
  4. Traceback okurken başlangıçta hangi iki bilgiyi aramak yararlıdır?
  5. Mantık hatası neden her zaman istisna üretmez?
  6. import math ne yapar?
  7. Kendi modülünüzü oluşturmanın temel amacı nedir?
  8. Main guard hangi iki kullanım biçimini ayırmaya yardım eder?
  9. Hata ayıklamada “tek değişiklik yapıp yeniden denemek” neden yararlıdır?

Bir sonraki bölümde yeni kavram eklemeyeceğiz. İlk 13 haftadaki araçları birlikte kullanarak küçük bir bütünleştirici mikro uygulama geliştireceğiz.

Back to top