Dosyalar, istisnalar ve sistematik hata ayıklama

Gerçek programlar yalnızca doğru girdilerle çalışmaz. Dosya bulunmayabilir, kullanıcı sayı yerine metin girebilir, kayıt eksik olabilir veya program mantıksal olarak yanlış sonuç üretebilir. Bu hafta temel dosya işlemlerini ve istisnaları kullanacak; hata mesajını rastgele değişiklik yapma nedeni değil, teşhis kanıtı olarak okuyacağız.

Bu hafta neleri yapabilmelisiniz?

  • with open(...) ile metin dosyası okuyup yazabilmeli,
  • "r", "w" ve "a" modlarının farkını açıklayabilmeli,
  • Türkçe metin dosyalarında neden encoding="utf-8" kullandığımızı açıklayabilmeli,
  • Dosya üzerinde for line in file ile satır satır dolaşabilmeli,
  • Satır sonlarını ve basit ayırıcıyla veri işlemeyi yönetebilmeli,
  • Söz dizimi, çalışma zamanı ve mantık hatalarını ayırabilmeli,
  • raise ile beklenmeyen/geçersiz durumu açık bir istisna olarak üretebilmeli,
  • try / except, except ... as error, else ve finally rollerini ayırabilmeli,
  • Hata iz dökümünün (traceback) en alttaki hata satırını ve çağrı zincirini okuyabilmeli,
  • Hatayı küçük bir örnekle yeniden üretebilmelisiniz.

Dosyayı güvenli biçimde açmak

with open("notes.txt", "r", encoding="utf-8") as file:
    content = file.read()

print(content)

with bloğu tamamlandığında dosya kapatılır. Elle close() çağrısını unutma riski azalır.

Dosya modları: r, w, a

Mod Anlam Kritik davranış
"r" oku dosya yoksa FileNotFoundError
"w" yaz dosya varsa mevcut içeriği silip baştan yazar
"a" sona ekle mevcut içeriği korur, sonuna yazar
Warning"w" modu yıkıcıdır
with open("odev.txt", "w", encoding="utf-8") as file:
    file.write("Yeni içerik")

odev.txt zaten varsa eski içerik korunmaz. Var olan dosyaya ekleme yapmak istiyorsanız "a" kullanın. Dosya modunu yazmadan önce “mevcut içeriği korumalı mıyım?” sorusunu sorun.

Web/Pyodide ortamında sanal dosya sistemi kullanılabildiği için küçük dosya deneylerini doğrudan yapabiliriz:

Neden encoding="utf-8" yazıyoruz?

Metin dosyası baytlardan oluşur; karakterlerin bu baytlara nasıl çevrileceğini bir karakter kodlaması belirler. İşletim sisteminin varsayılan kodlamasına güvenmek, özellikle Windows’ta ve Türkçe ç, ğ, ı, İ, ö, ş, ü karakterlerinde farklı makinelerde farklı sonuç üretebilir.

Bu ders boyunca metin dosyalarında açıkça:

encoding="utf-8"

kullanacağız. Böylece dosyanın hangi kodlamayla okunup yazıldığı makine varsayımına bırakılmaz.

Satır satır okuma

Büyük veya satır-temelli dosyalarda bütün dosyayı read() ile belleğe almak zorunda değiliz. Dosya nesnesi doğrudan dolaşılabilir:

Bu derste for line in file temel yaklaşımımızdır. readlines() bütün satırları listeye yükler; küçük dosyalarda çalışır ama çoğu satır-temelli işlem için gerekli değildir.

Üç hata sınıfını ayırın

Söz dizimi hatası

if score > 50
    print("Geçti")

Python kodun yapısını okuyamaz.

Çalışma zamanı hatası

score = int("seksen")

Kod okunur, ancak yürütme sırasında sorun oluşur.

Mantık hatası

# Yüzde 20 indirim yapmak isterken:
final = price * 1.20

Program çalışır fakat yanlış sonuç üretir. Mantık hataları çoğu zaman istisna üretmediği için test ve örnek girdiyle doğrulama gerektirir.

Hata üretmek: raise

Bazı hatalar Python tarafından kendiliğinden oluşur (int("abc")ValueError). Bazı geçersiz durumları ise bizim iş kuralımız belirler. Bu durumda raise ile açıkça istisna üretiriz:

raise ValueError(...) çalıştığı anda normal akış kesilir ve uygun except bloğu aranır. raise ile print("hata") aynı şey değildir: print yalnızca mesaj yazar; raise çağırana işlemin normal biçimde tamamlanamadığını bildirir.

Bu mekanizma 6–12. haftalarda nesnelerin invariant’larını ve alan kurallarını korumak için temel araç olacaktır.

try / except: hatayı saklamak için değil yönetmek için

as error, yakalanan istisna nesnesine erişmemizi sağlar; böylece hata mesajını veya ayrıntısını gerektiğinde kullanabiliriz.

except Exception: yazarak her şeyi yakalamak başlangıçta rahat görünebilir; ancak programlama hatalarını da saklayabilir. Beklediğiniz hata türünü mümkün olduğunca açık yazın.

try bloğunda hata oluşup oluşmamasına göre except ve normal akışın nasıl ilerlediğini gösteren diyagram.

try/except akışı
WarningSessizce yutmayın
try:
    risky_operation()
except Exception:
    pass

bu biçim, neyin yanlış gittiğini görünmez hâle getirir. Öğrenme aşamasında hata mesajını görmek çoğu zaman daha değerlidir.

else ve finally

Tam sözdizimi şu parçaları içerebilir:

try:
    value = int(raw)
except ValueError as error:
    print("Dönüşüm başarısız:", error)
else:
    print("Dönüşüm başarılı:", value)
finally:
    print("Bu satır her durumda çalışır")
  • else, try bloğu istisnasız tamamlanırsa çalışır.
  • finally, istisna oluşsa da oluşmasa da çalışır; kaynak temizleme gibi “her durumda yapılması gereken” işler için kullanılır.

Dosya açma/kapamada çoğu zaman with kullandığımız için elle finally ile close() yazmamıza gerek kalmaz. Yine de finally kavramını tanımak önemlidir.

Hata iz dökümü (traceback) nasıl okunur?

Traceback (most recent call last):
  File "app.py", line 12, in <module>
    total = calculate(data)
  File "app.py", line 5, in calculate
    return 100 / data["count"]
ZeroDivisionError: division by zero

İlk bakılacak yer en alttaki satırdır: hata türü ZeroDivisionError. Sonra hemen üstte hatanın oluştuğu kod satırını bulun. Daha yukarıdaki satırlar o noktaya nasıl gelindiğini gösterir.

Bir Python traceback çıktısında dosya, satır, çağrı zinciri ve hata türünün nasıl okunacağını gösteren diyagram.

Traceback okuma

Hata ayıklama döngüsü

  1. Yeniden üret: Hata hangi girdide kesin oluşuyor?
  2. Daralt: En küçük hangi kod parçası hâlâ hatayı üretiyor?
  3. Kanıt topla: traceback, print, type, ara değerler.
  4. Hipotez kur: Sorunun nedeni ne olabilir?
  5. Tek değişiklik yap: Birden çok rastgele değişiklik yapma.
  6. Yeniden doğrula: Hata gitti mi? Başka durum bozuldu mu?

Bu süreç Nesne II dersinde debugger, logging ve testlerle daha sistematik hâle gelecektir.

Alıştırma — Doğru hata türünü yakala

except ValueError as error:

Alıştırma — Kendi hatanı üret

Aşağıdaki fonksiyonu tamamlayın. score 0–100 dışında ise ValueError üretmeli; geçerliyse değeri döndürmelidir:

-1, 0, 100, 101 sınırlarını ayrı ayrı deneyin.

Alıştırma — Mantık hatasını bul

Program hatasız çalışıyor. Ancak yüzde 20 indirim için beklenen sonuç 80 olmalı. Hatanın neden istisna üretmediğini açıklayın ve fonksiyonu düzeltin.

Alıştırma — Dosya verisini doğrula

Kodu önce okuyun; hangi satırın atlanacağını tahmin edin. Daha sonra bozuk satırın yerine 30 yazarak tekrar çalıştırın.

Küçük üretim görevi — Dayanıklı kayıt okuyucu

name,score biçiminde dört satırlık bir dosya oluşturun. Bir satırda geçersiz puan bulunsun. Program:

  • Dosyayı encoding="utf-8" ile açsın,
  • for line in file ile dolaşsın,
  • Geçerli kayıtları listeye eklesin,
  • Geçersiz satırı except ValueError as error ile açık mesajla bildirsin,
  • Geçerli puanların ortalamasını yazsın,
  • Hiç geçerli kayıt yoksa sıfıra bölme hatasına düşmesin,
  • Sonuç raporunu ayrı bir dosyaya yazarken "w" modunun eski içeriği sileceğini bilinçli olarak kullansın.

Çalışır kod teslimi: Normal dosya, bir bozuk satırlı dosya ve hiç geçerli kayıt içermeyen dosya olmak üzere üç senaryoyu çalıştırın. Her senaryoda program kontrollü biçimde tamamlanmalıdır.

Python köprüsünün kapanışı

İlk beş haftada Python’ın temel araçlarını hızlı biçimde kurduk. Bundan sonra dersin odağı değişiyor. Artık soru “Python’da döngü nasıl yazılır?” değil, “Bir problem alanındaki durumu ve davranışı hangi nesne üstlenmeli?” olacaktır.

Elimizde şu beceriler var:

  • Değer ve türlerle çalışma,
  • Kontrol akışı,
  • Koleksiyonlar ve referans sezgisi,
  • Fonksiyonla sorumluluk ayırma,
  • Modül, dosya ve hata yönetimi,
  • Geçersiz durumu raise ile çağırana bildirme.

Bunların tamamı OOP’nin ham malzemesidir.

Kendinizi kontrol edin

  1. with open(...) neden tercih edilir?
  2. "w" ile "a" arasındaki kritik fark nedir?
  3. Neden açıkça encoding="utf-8" yazıyoruz?
  4. raise ile print("hata") arasındaki fark nedir?
  5. except ValueError as error içindeki error neyi temsil eder?
  6. else ve finally hangi koşullarda çalışır?
  7. Mantık hatası neden traceback üretmeyebilir?
  8. Hata ayıklamada “en küçük yeniden üretim” ne sağlar?

Bu haftadan akılda kalması gerekenler

  • Hata mesajı bir kanıttır; rastgele değişiklik çağrısı değildir.
  • raise, geçersiz durumu normal akıştan ayırıp çağırana bildirir.
  • Beklenen istisnayı dar biçimde yakalayın ve gerektiğinde as error ile ayrıntısını kullanın.
  • "w" mevcut dosyayı sıfırlar; "a" sona ekler.
  • Türkçe dâhil taşınabilir metin işlemleri için kodlamayı açıkça utf-8 seçin.
  • Dosya satırlarını çoğu zaman doğrudan for line in file ile dolaşabilirsiniz.
  • Çalışan kod doğru kod olmak zorunda değildir.
  • Gelecek haftadan itibaren bu kuralları nesnelerin içinde korumayı öğreneceğiz.
Back to top