Python'da hata yönetimi try ve except ile yapılır: hata çıkarabilecek kodu try bloğuna yazarsınız, bir istisna (exception) oluşursa Python o bloğun geri kalanını atlar ve türü eşleşen except bloğunu çalıştırır. İsteğe bağlı else bloğu yalnızca hiç istisna çıkmadığında, finally bloğu ise her durumda çalışır. İstisnayı yakalarsanız program çökmez, try deyiminden sonraki satırdan devam eder.

Bu yazıda dört bloğun ne zaman ve hangi sırayla çalıştığını, birden fazla except yazarken sıranın neden önemli olduğunu, Python 3.14 ile gelen parantezsiz except A, B: yazımını, finally içinde return kullanınca çıkan yeni uyarıyı ve sık yapılan hataları Python 3.15.2 ile çalıştırılmış örneklerle anlatıyoruz. Kendi istisnanızı fırlatmak ve tanımlamak ayrı bir konudur; onu raise ve özel istisna yazısında ele alıyoruz. Python'a yeni başlıyorsanız konu sırası için Python öğrenme rehberi sayfasına bakabilirsiniz.

Sözdizimi hatası ile istisna arasındaki fark

Python belgeleri hataları iki gruba ayırır. Birincisi sözdizimi hatasıdır (SyntaxError): kod daha çalışmadan, ayrıştırma aşamasında yakalanır. Örneğin bir if satırının sonunda iki nokta unutulursa dosyanın hiçbir satırı çalışmaz. Bu tür hataları try ile yakalayamazsınız, çünkü try deyiminin kendisi de henüz çalışmamıştır; kodu düzeltmeniz gerekir.

İkinci grup istisnalardır: sözdizimi doğru olan bir ifade çalışırken bir sorunla karşılaşır. Sıfıra bölme, olmayan bir sözlük anahtarını okuma ya da sayıya çevrilemeyen bir metni int() ile dönüştürme bu türdendir. İstisnalar kendiliğinden ölümcül değildir; yakalamazsanız program durur ve bir traceback (hata izi) basılır. Traceback'in son satırı istisnanın türünü ve ayrıntısını gösterir, üst satırlar ise hatanın hangi dosyada ve hangi satırda oluştuğunu sırasıyla listeler:

python
fiyatlar = {"elma": 30}
print(fiyatlar["armut"])
# hata: KeyError: 'armut'
text
Traceback (most recent call last):
  File "ornek.py", line 2, in <module>
    print(fiyatlar["armut"])
          ~~~~~~~~^^^^^^^^^
KeyError: 'armut'

Yukarıdaki çıktı Python 3.15.2'nin gerçek çıktısıdır; yalnızca uzun geçici dosya yolunu ornek.py olarak kısalttık. ~ ve ^ işaretleri satırın tam olarak hangi parçasının hataya yol açtığını gösterir. Sık karşılaşılan hata mesajlarının ne anlama geldiğini Python sık hata mesajları yazısında tek tek açıklıyoruz.

try ve except ile ilk örnek

En basit kullanımda try bloğuna riskli kodu, except satırına da yakalamak istediğiniz istisna sınıfını yazarsınız. as ile verdiğiniz isim, yakalanan istisna nesnesine bağlanır; bu nesneyi print() ile yazdırdığınızda hatanın açıklaması görünür:

python
metin = "12a"
try:
    sayi = int(metin)
    print("Bu satır çalışmaz")
except ValueError as hata:
    print("Geçersiz giriş:", hata)
print("Program devam ediyor")
# çıktı: Geçersiz giriş: invalid literal for int() with base 10: '12a'
# çıktı: Program devam ediyor

Akış şöyle işler: önce try bloğu çalışır. int(metin) bir ValueError fırlattığı anda bloğun kalanı (buradaki ikinci print) atlanır. Python sonra except satırlarına sırayla bakar; istisnanın türü yazılan sınıfla ya da onun bir alt sınıfıyla eşleşirse o blok çalışır. Blok bittikten sonra program try deyiminin tamamından sonraki satıra geçer. Hiçbir except eşleşmezse istisna bir dıştaki try deyimine, oradan da çağıran fonksiyona doğru yükselir; hiçbir yerde yakalanmazsa program traceback ile sonlanır.

Bir try bloğu yalnızca kendi içinde yazılan satırları değil, o satırların çağırdığı fonksiyonların içinde oluşan istisnaları da yakalar. Bu sayede hatayı, onu anlamlı biçimde ele alabileceğiniz katmanda yakalayabilirsiniz:

python
def ortalama(sayilar):
    return sum(sayilar) / len(sayilar)

def rapor(sayilar):
    try:
        print("Ortalama:", ortalama(sayilar))
    except ZeroDivisionError:
        print("Liste boş, ortalama hesaplanamadı")

rapor([4, 6, 8])
rapor([])
# çıktı: Ortalama: 6.0
# çıktı: Liste boş, ortalama hesaplanamadı

Birden fazla except ve sıranın önemi

Birden fazla except ve sıranın önemi: ArithmeticError, LookupError, OSError, ValueError

Farklı hata türlerine farklı tepki vermek için aynı try deyimine birden fazla except yazabilirsiniz. Belgelere göre en fazla bir except bloğu çalışır ve ilk eşleşen kazanır. Ayrıca bir except bloğunun içinde çıkan yeni bir istisna, aynı deyimin diğer except blokları tarafından yakalanmaz:

python
def bol(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return "sıfıra bölünemez"
    except TypeError as e:
        return f"tip hatası: {e}"

print(bol(10, 4))
print(bol(1, 0))
print(bol("10", 2))
# çıktı: 2.5
# çıktı: sıfıra bölünemez
# çıktı: tip hatası: unsupported operand type(s) for /: 'str' and 'int'

Eşleşme sınıf hiyerarşisine göre yapılır: except LookupError yazarsanız LookupError'dan türeyen KeyError ve IndexError da yakalanır. Tersi geçerli değildir; except KeyError bir IndexError'ı yakalamaz. Bu yüzden genel sınıfı özel sınıftan önce yazarsanız özel blok hiçbir zaman çalışmaz:

python
veri = {"ad": "Ayşe"}
try:
    veri["yas"]
except LookupError:
    print("LookupError bloğu yakaladı")
except KeyError:
    print("Bu satıra hiç gelinmez")
# çıktı: LookupError bloğu yakaladı

Kural basittir: except bloklarını en özelden en genele doğru sıralayın. Dosya işlemlerinde bu, önce FileNotFoundError veya PermissionError, en sonda da onların ortak atası OSError yazmak demektir. Sık karşılaşılan hiyerarşi şöyledir:

Genel sınıfKapsadığı bazı alt sınıflarTipik durum
ArithmeticErrorZeroDivisionError, OverflowErrorMatematik işlemi hataları
LookupErrorKeyError, IndexErrorOlmayan anahtar ya da sıra numarası
OSErrorFileNotFoundError, PermissionError, FileExistsError, TimeoutErrorDosya, ağ ve işletim sistemi hataları
ValueErrorUnicodeDecodeError, UnicodeEncodeErrorTürü doğru ama değeri uygunsuz veri
NameErrorUnboundLocalErrorTanımsız ya da henüz atanmamış isim
ExceptionYukarıdakilerin hepsiSistemden çıkış dışındaki neredeyse tüm hatalar

Tek except ile birden fazla tür: Python 3.14 yazımı

Birkaç türe aynı tepkiyi vermek istiyorsanız türleri tek bir except satırında toplayabilirsiniz. Klasik yazım türleri parantez içinde bir demet (tuple) olarak vermektir: except (ValueError, TypeError):. Python 3.14 ile gelen PEP 758 sayesinde, as kullanmadığınız sürece parantezleri yazmak zorunlu değildir:

python
def donustur(deger):
    try:
        return int(deger)
    except ValueError, TypeError:
        return None

print(donustur("42"), donustur("kırk"), donustur(None))
# çıktı: 42 None None

Burada "kırk" metni ValueError, None ise TypeError üretir; ikisi de aynı bloğa düşer. İstisna nesnesine bir isim vermek istediğinizde parantez hâlâ zorunludur. except ValueError, TypeError as e: yazarsanız Python 3.15.2 kodu hiç çalıştırmadan şu sözdizimi hatasını verir:

python
kod = """
try:
    pass
except ValueError, TypeError as e:
    pass
"""
try:
    compile(kod, "ornek.py", "exec")
except SyntaxError as e:
    print(e.msg)
# çıktı: multiple exception types must be parenthesized when using 'as'

Doğru yazım except (ValueError, TypeError) as e: biçimindedir. Parantezsiz yazım yalnızca 3.14 ve sonrasında geçerlidir; kodunuz daha eski sürümlerde de çalışacaksa parantezli biçimi kullanmaya devam edin. Sürüm farklarının toplu listesi için Python 3.15 yenilikleri yazısına bakabilirsiniz.

else bloğu: yalnızca hata çıkmazsa çalışan kod

else bloğu: yalnızca hata çıkmazsa çalışan kod

else bloğu tüm except bloklarından sonra yazılır ve try bloğu hiçbir istisna üretmeden, return, break veya continue ile ayrılmadan bittiğinde çalışır. Asıl faydası, korumak istediğiniz kodu küçük tutmanızdır. Belgeler de bunu önerir: başarı durumunda çalışacak satırları try içine eklemek yerine else'e koymak, beklemediğiniz bir istisnayı yanlışlıkla yakalamanızı önler. Ayrıca else içinde çıkan bir istisna, aynı deyimdeki except blokları tarafından yakalanmaz:

python
def kare_yaz(metin):
    try:
        sayi = int(metin)
    except ValueError:
        print(f"{metin!r} sayı değil")
    else:
        print(f"{metin!r} geçerli, karesi {sayi ** 2}")

kare_yaz("7")
kare_yaz("yedi")
# çıktı: '7' geçerli, karesi 49
# çıktı: 'yedi' sayı değil

finally bloğu: her durumda çalışan temizlik kodu

finally bloğu, try deyimi tamamlanmadan önce yapılan son iştir ve istisna çıksa da çıkmasa da çalışır. İstisna hiçbir except tarafından yakalanmadıysa Python onu geçici olarak saklar, finally bloğunu çalıştırır ve sonra istisnayı yeniden fırlatır. Aynı durum except veya else bloğunun içinde çıkan istisnalar için de geçerlidir. Aşağıdaki örnekte üç senaryoyu birlikte görüyorsunuz:

python
def islem(x, y):
    try:
        sonuc = x / y
    except ZeroDivisionError:
        print("sıfıra bölme!")
    else:
        print("sonuç:", sonuc)
    finally:
        print("finally çalıştı")

islem(6, 3)
islem(6, 0)
try:
    islem("6", "3")
except TypeError as e:
    print("dışarıda yakalandı:", e)
# çıktı: sonuç: 2.0
# çıktı: finally çalıştı
# çıktı: sıfıra bölme!
# çıktı: finally çalıştı
# çıktı: finally çalıştı
# çıktı: dışarıda yakalandı: unsupported operand type(s) for /: 'str' and 'str'

Üçüncü çağrıda iki metni bölmek TypeError üretir. Bu tür için bir except olmadığından önce finally çalışır, ardından istisna fonksiyonun dışına çıkar ve dıştaki try tarafından yakalanır. try bloğu return, break veya continue ile ayrılırken de finally o adımdan hemen önce çalışır:

python
def ayarlari_oku():
    try:
        print("try içinde")
        return "ayarlar"
    finally:
        print("finally, return'den hemen önce")

print(ayarlari_oku())
# çıktı: try içinde
# çıktı: finally, return'den hemen önce
# çıktı: ayarlar

Gerçek uygulamalarda finally dosya, ağ bağlantısı ya da kilit gibi dış kaynakları serbest bırakmak için kullanılır. Ancak dosyalar ve kilitler gibi nesneler zaten with deyimini destekler; çoğu durumda try ile finally yazmak yerine with ve context manager kullanmak daha kısa ve daha güvenlidir.

finally içinde return: Python 3.14 uyarısı

finally bloğu return, break veya continue çalıştırırsa saklanan istisna sessizce yok olur. try içindeki return değeri de ezilir, çünkü bir fonksiyonun dönüş değeri en son çalışan return ile belirlenir. Bu davranış kafa karıştırıcı olduğu için Python 3.14 ile gelen PEP 765 derleyicinin bu durumda SyntaxWarning vermesini sağladı. Aşağıdaki dosyayı çalıştırdığınızda uyarı, kod daha derlenirken standart hata akışına yazılır:

python
def durum():
    try:
        return True
    finally:
        return False

print(durum())
# çıktı: False
text
ornek.py:5: SyntaxWarning: 'return' in a 'finally' block
  return False
False

Fonksiyon True yerine False döndürür. Daha tehlikeli olanı, try içinde oluşan bir istisnanın da aynı şekilde kaybolmasıdır: try: 1/0 ve finally: return 42 yazılmış bir fonksiyon hata vermeden 42 döndürür. Çözüm, dönüş değerini finally dışında belirlemek ve finally bloğunu yalnızca temizlik için kullanmaktır.

İstisna nesnesi: as ile gelen değişken ve sys.exception()

except ... as isim ile bağlanan değişken, blok bitince Python tarafından silinir. Dil referansı bunun nedenini şöyle açıklar: traceback'i taşıyan istisna nesnesi, bulunduğu çerçeveyle bir referans döngüsü kurar ve bu döngü tüm yerel değişkenleri gereğinden uzun süre bellekte tutar. İstisnayı bloktan sonra kullanmak istiyorsanız onu başka bir isme atayın:

python
try:
    1 / 0
except ZeroDivisionError as hata:
    saklanan = hata
print(type(saklanan).__name__)
print("hata" in dir())
# çıktı: ZeroDivisionError
# çıktı: False

Yerleşik istisnaların args özelliği yapıcıya verilen argümanları bir demet olarak saklar; str() ise bu argümanları okunur biçimde döndürür. except bloğunun içindeyken o an işlenen istisnaya sys.exception() ile de ulaşabilirsiniz; bloktan çıkınca bu değer önceki hâline (çoğunlukla None) döner:

python
import sys

try:
    int("on")
except ValueError as e:
    print(e.args)
    print(repr(sys.exception()))
print(sys.exception())
# çıktı: ("invalid literal for int() with base 10: 'on'",)
# çıktı: ValueError("invalid literal for int() with base 10: 'on'")
# çıktı: None

Exception, BaseException ve çıplak except

Tüm istisnaların ortak atası BaseException'dır. Onun alt sınıfı Exception, programı sonlandırmaya yönelik olmayan tüm yerleşik hataların atasıdır. sys.exit() çağrısının fırlattığı SystemExit ve kullanıcının Ctrl+C ile ürettiği KeyboardInterrupt ise bilerek Exception'ın dışında tutulmuştur; böylece except Exception yazan bir kod programın kapanmasını yanlışlıkla engellemez:

python
import sys

try:
    sys.exit(3)
except Exception:
    print("Exception bunu yakalamaz")
except SystemExit as e:
    print("SystemExit yakalandı, çıkış kodu:", e.code)
# çıktı: SystemExit yakalandı, çıkış kodu: 3

Hiç tür yazmadığınız bir except: satırı (çıplak except) her şeyi, SystemExit ve KeyboardInterrupt dahil, yakalar. Dil kuralına göre böyle bir blok varsa en sonda olmalıdır. Belgelerin önerisi, yakalayacağınız türleri olabildiğince özel seçmek ve beklemediğiniz istisnaların yukarı çıkmasına izin vermektir. Exception'ı yakalamanız gereken bir yer varsa (örneğin bir sunucunun en dış döngüsü), en yaygın desen hatayı kaydedip çıplak raise ile yeniden fırlatmaktır. Hata kaydı için logging modülü yazısına bakabilirsiniz.

EAFP ve LBYL: önce sor mu, sonra özür dile mi?

Python sözlüğü (glossary) iki kodlama tarzını tanımlar. LBYL ("look before you leap", atlamadan önce bak) tarzında işlemden önce koşulları if ile kontrol edersiniz. EAFP ("easier to ask for forgiveness than permission", izin istemekten özür dilemek kolaydır) tarzında ise işlemin başarılı olacağını varsayar, olmazsa istisnayı yakalarsınız. Sözlük, EAFP'nin Python'da yaygın olduğunu belirtir ve çok iş parçacıklı ortamlarda LBYL'nin kontrol ile işlem arasında bir yarış durumuna (race condition) açık olduğunu not eder: siz if ile kontrol ettikten sonra başka bir iş parçacığı veriyi değiştirebilir.

python
stok = {"kalem": 12}

# LBYL
if "silgi" in stok:
    adet = stok["silgi"]
else:
    adet = 0

# EAFP
try:
    adet2 = stok["silgi"]
except KeyError:
    adet2 = 0

print(adet, adet2, stok.get("silgi", 0))
# çıktı: 0 0 0

Son satırdaki dict.get() örneği de bir hatırlatmadır: bazı durumlar için dilin zaten istisnasız bir yolu vardır. Sözlükte varsayılan değer için get(), dosya silerken yokluğu önemsiz saymak için Path.unlink(missing_ok=True) gibi seçenekler try bloğundan daha okunurdur.

Uygulama: geçerli giriş gelene kadar tekrar sormak

Resmî öğreticideki klasik örnek, kullanıcı geçerli bir tam sayı girene kadar soruyu tekrarlayan bir döngüdür. Aşağıda input() yerine bir liste kullandık ki örnek kendi başına çalışabilsin; gerçek programda girdiler yerine input("Yaşınız: ") yazmanız yeterlidir. continue hatalı girişte döngünün başına döner, else ise yalnızca dönüşüm başarılı olduğunda çalışır:

python
girdiler = ["on iki", "", "-5", "12"]
for metin in girdiler:
    try:
        yas = int(metin)
        if yas < 0:
            raise ValueError("yaş negatif olamaz")
    except ValueError as e:
        print(f"{metin!r} geçersiz ({e})")
        continue
    else:
        print("Kaydedilen yaş:", yas)
        break
# çıktı: 'on iki' geçersiz (invalid literal for int() with base 10: 'on iki')
# çıktı: '' geçersiz (invalid literal for int() with base 10: '')
# çıktı: '-5' geçersiz (yaş negatif olamaz)
# çıktı: Kaydedilen yaş: 12

Negatif sayı kontrolünde kendimiz bir ValueError fırlattık; böylece iki farklı geçersizlik nedeni aynı except bloğunda toplandı. raise deyiminin ayrıntıları, kendi istisna sınıfınızı yazmak ve hataları zincirlemek için Python raise rehberine geçebilirsiniz. Gerçek bir terminal programında kullanıcı Ctrl+C'ye basarsa KeyboardInterrupt oluşur; bu örnekte onu yakalamadığımız için program beklendiği gibi sonlanır.

Sık yapılan hatalar

Sık yapılan hatalar: Çok geniş try bloğu, Hatayı sessizce yutmak, Genel sınıfı önce yazmak, Çıplak except kullanmak
  • Çok geniş try bloğu: Fonksiyonun tamamını tek bir try içine almak, hangi satırın hatayı ürettiğini gizler ve beklemediğiniz istisnaları da yakalar. Yalnızca hata çıkabilecek satırı koruyun, devamını else'e taşıyın.
  • Hatayı sessizce yutmak: except Exception: pass programın yanlış veriyle devam etmesine yol açar. En azından hatayı kaydedin ya da yeniden fırlatın.
  • Genel sınıfı önce yazmak: except Exception veya except OSError özel türlerin üstündeyse alttaki bloklar ölü koda dönüşür.
  • Çıplak except kullanmak: except: Ctrl+C'yi ve sys.exit()'i de yakalar; programı kapatmak zorlaşır.
  • as ile parantezi unutmak: Python 3.14 ve sonrasında except A, B: geçerlidir ama except A, B as e: sözdizimi hatasıdır; parantez kullanın.
  • finally içinde return: İstisnaları ve asıl dönüş değerini yok eder; 3.14 itibarıyla SyntaxWarning verir.
  • except değişkenini bloktan sonra kullanmak: as e ile bağlanan isim blok sonunda silinir; sonradan erişirseniz NameError alırsınız.
  • SyntaxError'u try ile yakalamaya çalışmak: Aynı dosyadaki sözdizimi hatası kod çalışmadan önce bulunur; try ancak compile(), eval() veya içe aktarma gibi çalışma anında derlenen kodlardaki sözdizimi hatalarını yakalayabilir.

Sık Sorulan Sorular

Python'da tüm hataları nasıl yakalarım?

except Exception as e: sistemden çıkış dışındaki neredeyse tüm hataları yakalar. Bunu yalnızca programın en dış katmanında, hatayı kaydedip yeniden fırlatmak ya da kullanıcıya anlaşılır bir mesaj göstermek için kullanın. Çıplak except: ve except BaseException Ctrl+C ile çıkışı da engellediği için önerilmez.

else ile finally arasındaki fark nedir?

else yalnızca try bloğu hatasız bittiğinde çalışır ve içindeki istisnalar aynı deyimin except blokları tarafından yakalanmaz. finally ise hata olsun olmasın, hatta return ile çıkılırken bile her durumda çalışır.

Birden fazla istisna türünü tek satırda nasıl yakalarım?

Türleri parantez içinde verin: except (ValueError, TypeError) as e:. Python 3.14 ve sonrasında as kullanmıyorsanız except ValueError, TypeError: da yazabilirsiniz.

Hatayı yakalayıp yine de programın durmasını nasıl sağlarım?

except bloğunda gereken işi yaptıktan sonra argümansız raise yazın. Bu, aynı istisnayı traceback'iyle birlikte yeniden fırlatır. Ayrıntılar raise ve özel istisna yazısında.

try kullanmak programı yavaşlatır mı?

Belgeler bu konuda bir ölçüm vermez. Pratik ölçüt okunabilirliktir: koşul kontrolü kodu karmaşıklaştırıyorsa ya da yarış durumuna açıksa EAFP tarzı try, basit bir varsayılan değer yetiyorsa dict.get() gibi hazır araçlar daha uygundur.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Python öğrenme rehberi · Python raise ve özel istisna · Python with ve context manager · Python sık hata mesajları · Python dosya okuma ve yazma · Python logging modülü · Python 3.15 yenilikleri