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 bölümün kapsamı

NoteSınavda sorulur
  • with open(...) kalıbı ve dosyanın blok bitiminde kapanması
  • "r", "w" ve "a" kipleri; "w" kipinin yıkıcılığı
  • metin dosyalarında encoding="utf-8" gerekçesi
  • söz dizimi, çalışma zamanı ve mantık hatalarının ayrımı
  • raise ile print farkı
  • belirli istisna türünü yakalama ve sessiz yutmanın zararı
  • traceback çıktısını hata türünden başlayarak okuma
  • pathlib ve ikili dosya kipleri
  • logging modülü
  • kendi istisna sınıfını tanımlama (12. bölümün konusu)

Bu başlıklar konunun devamıdır; ileride karşınıza çıkar ama bu derste ezberlemeniz beklenmiyor.

Hata oluştuğunda programın ne yapmasını istiyoruz?

Bir hatayı ekrana yazmak, yakalayıp yutmak ve çağırana bildirmek üç ayrı karardır. Üçü de kod olarak birkaç satır tutar; aradaki fark, sorun çıktığında programın kime ne söylediğidir.

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.

Sınav provası

Önce kendi cevabını seç; sonra cevap anahtarında her şıkkın neden doğru veya yanlış olduğunu oku. Çeldiriciler uydurma değil, bu konuda gerçekten yapılan hatalardır. Sınav maddeleri de bu mantıkla yazılır.

Madde 1. with open("notes.txt", "r", encoding="utf-8") as file: yazımının elle open() / close() çiftine göre avantajı nedir?

  • A. Blok tamamlandığında dosya, hata oluşsa bile kapatılır; close() çağrısını unutma riski kalmaz
  • B. Dosya yoksa kendiliğinden oluşturur
  • C. Kodlama belirtmeyi gereksiz kılar
  • D. Dosyanın içeriğini kendiliğinden satırlara böler
  • E. Dosyayı belleğe tümüyle yükleyerek okumayı hızlandırır

Madde 2. İçinde veri bulunan odev.txt dosyası "w" kipiyle açılıp yazılırsa ne olur?

  • A. Dosya zaten var olduğu için FileExistsError yükselir
  • B. Mevcut içerik silinir ve dosya baştan yazılır
  • C. Yeni içerik dosyanın sonuna eklenir
  • D. Yeni içerik dosyanın başına eklenir, eskisi aşağı kayar
  • E. Dosya salt okunur açılır

Madde 3. Var olmayan bir dosyayı "r" kipiyle açmak ne yapar?

  • A. None döndürür
  • B. Boş bir dosya oluşturur
  • C. FileNotFoundError yükseltir
  • D. Boş metin döndürür
  • E. PermissionError yükseltir

Madde 4. Metin dosyalarında encoding="utf-8" yazmayı neden alışkanlık hâline getiriyoruz?

  • A. Dosya boyutunu küçülttüğü için
  • B. Dosya okuma hızını artırdığı için
  • C. Yalnızca ikili dosyalarda gerektiği için
  • D. Kodlama yazılmazsa işletim sisteminin varsayılanı kullanılır; aynı dosya farklı makinelerde Türkçe karakterlerde farklı okunabilir
  • E. Python 3’te zorunlu olduğu için

Madde 5. Yüzde 20 indirim yapmak isterken yazılan final = price * 1.20 satırı hangi hata sınıfına girer?

  • A. Çalışma zamanı hatası
  • B. Tür hatası
  • C. Söz dizimi hatası
  • D. Kodlama hatası
  • E. Mantık hatası; program çalışır ve istisna üretmez ama yanlış sonuç verir

Madde 6. raise ValueError("Yetersiz bakiye") ile print("Yetersiz bakiye") arasındaki temel fark nedir?

  • A. raise normal akışı keser ve çağırana işlemin tamamlanamadığını bildirir; print yalnızca ekrana mesaj yazar ve akış devam eder
  • B. raise mesajı kırmızı renkte yazar
  • C. raise yalnızca fonksiyon içinde, print yalnızca dışında kullanılır
  • D. print daha hızlı çalışır
  • E. raise programı her durumda sonlandırır

Madde 7. Aşağıdaki program ne yazdırır?

def withdraw(balance, amount):
    if amount > balance:
        raise ValueError("Yetersiz bakiye")
    return balance - amount

try:
    print(withdraw(100, 150))
except ValueError as error:
    print("İşlem reddedildi:", error)
  • A. İşlem reddedildi: None
  • B. İşlem reddedildi: Yetersiz bakiye
  • C. -50
  • D. Program çöker; except bloğu ValueError yakalayamaz
  • E. Önce -50, sonra İşlem reddedildi: Yetersiz bakiye

Madde 8. Aşağıdaki kalıp neden önerilmez?

try:
    risky_operation()
except Exception:
    pass
  • A. Exception sınıfı yakalanamaz
  • B. pass satırı sözdizimi hatası üretir
  • C. Beklenen hataların yanında programlama hatalarını da sessizce yutar; neyin yanlış gittiği görünmez olur
  • D. try bloğu except olmadan da yazılabildiği için gereksizdir
  • E. Programın yavaşlamasına neden olur

Madde 9. Bir traceback çıktısını okurken ilk nereye bakılır?

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
  • A. En üstteki satıra; hata orada oluşmuştur
  • B. Satır numaralarının en büyüğüne
  • C. Traceback (most recent call last): satırına
  • D. En alttaki satıra; hata türü ve mesajı oradadır, hemen üstündeki satır hatanın oluştuğu yerdir
  • E. Dosya adına; hata her zaman o dosyanın ilk satırındadır

Madde 10. Hata ayıklama döngüsünde “tek değişiklik yap” adımı neden önemlidir?

  • A. Tek değişiklik programı daha hızlı çalıştırır
  • B. Böylece traceback çıktısı kısalır
  • C. Sürüm denetimi yalnızca tek satırlık değişiklikleri kaydeder
  • D. Python aynı anda birden çok satırın değiştirilmesine izin vermez
  • E. Aynı anda birden çok şey değiştirilirse hangi değişikliğin işe yaradığı anlaşılamaz

Madde 1 — Doğru: A. with bloğu çıkışta temizliği kendisi yapar; istisna yükselse de dosya kapanır.

  • B yanlış: Dosyanın oluşup oluşmayacağını kip belirler; "r" kipinde hata yükselir.
  • C yanlış: encoding yine açıkça yazılmalıdır.
  • D yanlış: Satır dolaşımı ayrı bir işlemdir.
  • E yanlış: Okuma biçimini değiştirmez; yaptığı iş kaynak yönetimidir.

Madde 2 — Doğru: B. "w" kipi yıkıcıdır. Var olan içeriği korumak için "a" kullanılır.

  • A yanlış: "w" var olan dosyayı sorgusuz açar; hata vermez.
  • C yanlış: Bu "a" kipinin davranışıdır.
  • D yanlış: Böyle bir kip yoktur; başa ekleme elle yapılır.
  • E yanlış: Salt okuma kipi "r"dir.

Madde 3 — Doğru: C. Okuma kipi var olan bir dosya bekler; yoksa hata yükselir.

  • A yanlış: open() ya dosya nesnesi verir ya hata yükseltir.
  • B yanlış: Oluşturma davranışı "w" ve "a" kiplerine aittir.
  • D yanlış: Sessiz bir varsayılan yoktur.
  • E yanlış: Bu, erişim izni sorunlarında görülür; burada dosya hiç yoktur.

Madde 4 — Doğru: D. Karakterlerin baytlara çevrimi kodlamaya bağlıdır. Açıkça yazmak davranışı makine varsayımına bırakmaz.

  • A yanlış: Boyut içeriğe bağlıdır; amaç doğru çözümlemedir.
  • B yanlış: Kodlama seçimi bir başarım ayarı değildir.
  • C yanlış: İkili kipte kodlama parametresi kullanılmaz; bu bir metin kipi ayarıdır.
  • E yanlış: Zorunlu değildir; yazılmazsa varsayılan kullanılır ve sorun da buradadır.

Madde 5 — Doğru: E. Söz dizimi geçerli, yürütme sorunsuzdur. Hata ancak beklenen sonuçla karşılaştırınca görünür; bu yüzden test ve örnek girdi gerekir.

  • A yanlış: Yürütme sırasında istisna yükselmez.
  • B yanlış: İşlemdeki türler uyumludur.
  • C yanlış: Python satırı sorunsuz okur.
  • D yanlış: Karakter çözümlemesiyle ilgisi yoktur.

Madde 6 — Doğru: A. Mesaj yazmak çağıranın davranışını değiştirmez; program yanlış durumla yoluna devam eder. raise hatayı programın akışına taşır.

  • B yanlış: Görsel bir fark değil, akış farkıdır.
  • C yanlış: İkisi de her yerde kullanılabilir.
  • D yanlış: Hız ölçütü değildir.
  • E yanlış: Uygun bir except bloğu yakalarsa program devam eder.

Madde 7 — Doğru: B. raise çalıştığı anda return satırına hiç ulaşılmaz; akış except bloğuna geçer ve as error istisna nesnesini verir.

  • A yanlış: as error yakalanan istisna nesnesini bağlar; mesajı yazdırılır.
  • C yanlış: raise çalıştığı için çıkarma hiç yapılmaz.
  • D yanlış: Yükseltilen tür ValueErrordır ve blokla eşleşir.
  • E yanlış: İki dal birden çalışmaz; raise fonksiyondan çıkar.

Madde 8 — Doğru: C. except Exception çok geniştir ve pass hiçbir iz bırakmaz. Yazım hatası, yanlış ad veya tür hatası da aynı sessizliğe gömülür.

  • A yanlış: Yakalanabilir; zaten sorun bu kadar genişini yakalamaktır.
  • B yanlış: pass geçerli bir ifadedir; sorun anlamsaldır.
  • D yanlış: try tek başına yazılamaz; sorun yakalamanın genişliğindedir.
  • E yanlış: Sorun başarım değil, gizlenen bilgidir.

Madde 9 — Doğru: D. Traceback en dıştaki çağrıdan içeriye doğru yazılır. Hata türü sonda, hataya en yakın kod satırı hemen üstündedir.

  • A yanlış: En üst satır o noktaya nasıl gelindiğini gösteren ilk çağrıdır.
  • B yanlış: Numaraların büyüklüğü sırayı belirlemez; çağrı zinciri belirler.
  • C yanlış: Bu yalnızca başlıktır; bilgi taşımaz.
  • E yanlış: Satır numarası çıktıda zaten yazılıdır.

Madde 10 — Doğru: E. Neden–sonuç bağını kurmanın tek yolu değişkeni tek tutmaktır; aksi hâlde hata tesadüfen kaybolur ve geri gelir.

  • A yanlış: Başarımla ilgisi yoktur.
  • B yanlış: Traceback uzunluğu çağrı zincirine bağlıdır.
  • C yanlış: Böyle bir sınır yoktur.
  • D yanlış: Böyle bir kısıt yoktur; ilke yöntemle ilgilidir.

Tek sayfa özet

  • 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.

Bu bölümün kazanımları

Bu bölümü bitiren öğrenci:

  • with open(...) kalıbını kullanır ve dosyanın blok bitiminde kapandığını bilir.
  • "r", "w" ve "a" kiplerini ayırır ve "w" kipinin yıkıcı olduğunu belirler.
  • Metin dosyalarında encoding="utf-8" yazmanın gerekçesini açıklar.
  • Söz dizimi, çalışma zamanı ve mantık hatalarını birbirinden ayırır.
  • raise ile print farkını gösterir ve iş kuralı bozulduğunda istisna yükseltir.
  • Belirli bir istisna türünü yakalar ve sessiz yutmanın neden zararlı olduğunu açıklar.
  • Traceback çıktısını hata türünden başlayarak okur.
Back to top