Python'da bir istisnayı bilerek oluşturmak için raise deyimini kullanırsınız: raise ValueError("yaş negatif olamaz") yazdığınız anda çalışma durur ve istisna, onu yakalayacak bir except bulunana kadar çağrı yığınında yukarı çıkar. Kendi istisna türünüzü tanımlamak için Exception sınıfından türeyen bir sınıf yazmanız yeterlidir. Bir hatayı başka bir hataya dönüştürürken raise YeniHata(...) from eski yazımı, iki hatanın arasındaki neden sonuç ilişkisini traceback'te açıkça gösterir.
Bu yazıda raise deyiminin tüm biçimlerini, argümansız raise ile yeniden fırlatmayı, örtük ve açık istisna zincirlemeyi, from None ile zinciri gizlemeyi, kendi istisna hiyerarşinizi kurmayı, add_note() ile hataya not eklemeyi ve birden fazla hatayı birlikte bildiren ExceptionGroup ile except* yapısını Python 3.15.2'de çalıştırılmış örneklerle anlatıyoruz. Hataları yakalamanın temeli olan try, except, else ve finally için önce Python try except rehberini okumanızı öneririz. Tüm konu listesi Python öğrenme rehberi sayfasındadır.
raise deyiminin temel kullanımı

raise deyiminden sonra ya bir istisna örneği (instance) ya da bir istisna sınıfı yazılır. Bu sınıf BaseException'dan türemiş olmalıdır; pratikte neredeyse her zaman Exception veya onun bir alt sınıfıdır. Fonksiyonlarınızda geçersiz girdiyi reddetmenin en temiz yolu, durumu en iyi anlatan yerleşik türü seçip açıklayıcı bir mesajla fırlatmaktır:
def yas_dogrula(yas):
if not isinstance(yas, int):
raise TypeError(f"yaş tam sayı olmalı, {type(yas).__name__} verildi")
if yas < 0:
raise ValueError("yaş negatif olamaz")
return yas
for deger in (30, -2, "otuz"):
try:
print(yas_dogrula(deger))
except (TypeError, ValueError) as e:
print(f"{type(e).__name__}: {e}")
# çıktı: 30
# çıktı: ValueError: yaş negatif olamaz
# çıktı: TypeError: yaş tam sayı olmalı, str verildiBurada iki farklı yerleşik tür kullandık ve bu bir tercihtir: TypeError değerin türünün yanlış olduğunu, ValueError ise türün doğru ama değerin uygunsuz olduğunu anlatır. Çağıran kod bu ayrım sayesinde iki durumu farklı biçimde ele alabilir. Yerleşik türlerin ne zaman kullanıldığını belgelerdeki Built-in Exceptions sayfası tek tek açıklar; aşağıdaki tablo en sık ihtiyaç duyulanları özetler:
| Durum | Uygun tür | Örnek mesaj |
|---|---|---|
| Argümanın türü yanlış | TypeError | yaş tam sayı olmalı |
| Türü doğru, değeri geçersiz | ValueError | yaş negatif olamaz |
| Sözlükte anahtar yok | KeyError | anahtarın kendisi |
| Sıra numarası aralık dışında | IndexError | list index out of range |
| Alt sınıfın yazması gereken metot | NotImplementedError | alt sınıf bu metodu tanımlamalı |
| Hiçbir kategoriye uymayan hata | RuntimeError | durumu anlatan bir cümle |
Sınıf adını parantezsiz yazarsanız Python sınıfı argümansız çağırarak bir örnek oluşturur; yani raise ValueError ile raise ValueError() aynıdır. Bu durumda mesaj boş kalır, bu yüzden kullanıcıya gösterilecek hatalarda bir açıklama vermek daha yararlıdır:
try:
raise ValueError
except ValueError as e:
print(repr(e), repr(str(e)))
# çıktı: ValueError() ''Argümansız raise: yakaladığınız hatayı yeniden fırlatmak
Bir except bloğunun içinde tek başına raise yazarsanız, o an işlenmekte olan istisna (belgelerdeki adıyla etkin istisna) olduğu gibi, orijinal traceback'iyle birlikte yeniden fırlatılır. Bu desen, hatayı kaydetmek ya da kısmi bir temizlik yapmak isteyip asıl kararı üst katmana bırakmak istediğinizde kullanılır:
def ayar_oku(ayarlar):
try:
return ayarlar["port"]
except KeyError:
print("Hata kaydedildi, yeniden fırlatılıyor")
raise
try:
ayar_oku({})
except KeyError as e:
print("Üst katman yakaladı:", repr(e))
# çıktı: Hata kaydedildi, yeniden fırlatılıyor
# çıktı: Üst katman yakaladı: KeyError('port')raise e yazmak da çalışır, ancak argümansız raise niyetinizi daha açık anlatır ve yeni bir satır numarası eklemez. Etkin bir istisna yokken argümansız raise çalıştırırsanız dil kuralına göre bir RuntimeError oluşur:
try:
raise
except RuntimeError as e:
print(e)
# çıktı: No active exception to reraiseÖrtük zincirleme: hatayı işlerken çıkan yeni hata
Bir except veya finally bloğunun içinde yeni bir istisna oluşursa Python, işlenmekte olan eski istisnayı yeni istisnanın __context__ özelliğine otomatik olarak bağlar. Yakalanmayan yeni hata ekrana basılırken iki traceback birden görünür ve aralarında "During handling of the above exception, another exception occurred" cümlesi yer alır:
ayarlar = {}
try:
port = ayarlar["port"]
except KeyError:
raise RuntimeError("ayar dosyasında port yok")
# hata: RuntimeError: ayar dosyasında port yokTraceback (most recent call last):
File "ornek.py", line 3, in <module>
port = ayarlar["port"]
~~~~~~~^^^^^^^^
KeyError: 'port'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "ornek.py", line 5, in <module>
raise RuntimeError("ayar dosyasında port yok")
RuntimeError: ayar dosyasında port yokBu çıktı okuyana şunu söyler: ikinci hata, birincisi işlenirken beklenmedik biçimde ortaya çıktı. Oysa buradaki RuntimeError bir kaza değil, bilinçli bir dönüştürmedir. Bu ilişkiyi doğru anlatmak için açık zincirleme kullanılır.
raise from: açık zincirleme

raise YeniHata(...) from eski_hata yazdığınızda eski istisna yeni istisnanın __cause__ özelliğine atanır ve traceback'teki ara cümle "The above exception was the direct cause of the following exception" olur. Bu, alt katmandaki teknik bir hatayı (dosya yok, anahtar yok) üst katmanın anlayacağı bir hataya çevirirken kullanılan standart yoldur:
def port_al(ayarlar):
try:
return int(ayarlar["port"])
except KeyError as e:
raise RuntimeError("ayar dosyasında port yok") from e
port_al({})
# hata: RuntimeError: ayar dosyasında port yokTraceback (most recent call last):
File "ornek.py", line 3, in port_al
return int(ayarlar["port"])
~~~~~~~^^^^^^^^
KeyError: 'port'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "ornek.py", line 7, in <module>
port_al({})
~~~~~~~^^^^
File "ornek.py", line 5, in port_al
raise RuntimeError("ayar dosyasında port yok") from e
RuntimeError: ayar dosyasında port yokfrom sonrasında bir istisna örneği ya da None yazılabilir. from None, eski istisnayı ekran çıktısından gizler: Python __suppress_context__ özelliğini True yapar ve traceback'te yalnızca yeni hata görünür. Eski hata yine de __context__ içinde saklanır, yani hata ayıklarken ona ulaşabilirsiniz. Belgeler bunu örneğin bir KeyError'ı AttributeError'a çeviren sınıflarda kullanışlı bulur:
class Ayarlar:
def __init__(self, veri):
self._veri = veri
def __getattr__(self, ad):
try:
return self._veri[ad]
except KeyError:
raise AttributeError(f"{ad!r} adlı ayar yok") from None
a = Ayarlar({"tema": "koyu"})
print(a.tema)
try:
a.dil
except AttributeError as e:
print(e)
print(repr(e.__cause__), repr(e.__context__), e.__suppress_context__)
# çıktı: koyu
# çıktı: 'dil' adlı ayar yok
# çıktı: None KeyError('dil') TrueÜç özelliğin ilişkisi şöyledir: __context__ her zaman otomatik dolar; __cause__ yalnızca from ile dolar; __suppress_context__ ise from kullanıldığında True olur. Varsayılan traceback gösterimi __cause__ varsa onu her zaman gösterir, __context__'i ise yalnızca __cause__ boşsa ve gizleme bayrağı kapalıysa gösterir.
| Yazım | __cause__ | __suppress_context__ | Traceback'te görünen |
|---|---|---|---|
| except içinde raise Yeni() | None | False | İki hata, "During handling..." ile |
| raise Yeni() from eski | eski | True | İki hata, "direct cause" ile |
| raise Yeni() from None | None | True | Yalnızca yeni hata |
Kendi istisna sınıfınızı tanımlamak
Belgeler kullanıcı tanımlı istisnaların doğrudan ya da dolaylı olarak Exception'dan türetilmesini, BaseException'dan türetilmemesini önerir. İsimlerin standart istisnalar gibi Error ile bitmesi yaygın bir gelenektir. Bir uygulama ya da kütüphane yazıyorsanız önce tek bir temel sınıf, sonra ondan türeyen özel sınıflar tanımlamak işe yarar: çağıran kod isterse yalnızca temel sınıfı yakalayarak sizin tüm hatalarınızı tek seferde ele alabilir.
class UygulamaHatasi(Exception):
"""Bu uygulamadaki tüm hataların atası."""
class StokHatasi(UygulamaHatasi):
def __init__(self, urun, istenen, mevcut):
super().__init__(f"{urun}: {istenen} adet istendi, stokta {mevcut} var")
self.urun = urun
self.istenen = istenen
self.mevcut = mevcut
class OdemeHatasi(UygulamaHatasi):
pass
def siparis_ver(urun, adet, stok):
mevcut = stok.get(urun, 0)
if adet > mevcut:
raise StokHatasi(urun, adet, mevcut)
return "onaylandı"
stok = {"defter": 3}
try:
siparis_ver("defter", 5, stok)
except StokHatasi as e:
print(e)
print("Eksik:", e.istenen - e.mevcut)
try:
raise OdemeHatasi("kart reddedildi")
except UygulamaHatasi as e:
print(type(e).__name__, "-", e)
# çıktı: defter: 5 adet istendi, stokta 3 var
# çıktı: Eksik: 2
# çıktı: OdemeHatasi - kart reddedildiBu örnekte iki ayrıntı önemlidir. Birincisi, StokHatasi sorunla ilgili verileri (urun, istenen, mevcut) özellik olarak taşır; böylece onu yakalayan kod mesajı ayrıştırmak zorunda kalmaz. Belgeler de istisna sınıflarını basit tutmayı, genellikle yalnızca bu tür bilgi taşıyan özellikler eklemeyi önerir. İkincisi, __init__ içinde super().__init__(mesaj) çağrısı yapılmıştır. Bu çağrıyı unutursanız args demeti yapıcıya verdiğiniz ham argümanlarla dolar ve str(e) anlamsız bir demet metni döndürür:
class EksikHata(Exception):
def __init__(self, urun, adet):
self.urun = urun
self.adet = adet
e = EksikHata("defter", 5)
print(str(e))
print(e.args)
# çıktı: ('defter', 5)
# çıktı: ('defter', 5)Mesajı tamamen kendiniz biçimlendirmek istiyorsanız __str__ metodunu da ezebilirsiniz. Belgeler ayrıca aynı anda birden fazla yerleşik istisna türünden kalıtım almamanızı önerir: bazı yerleşik türlerin bellek düzeni farklıdır ve args yönetimleri çakışabilir. Sınıf yazımı ve kalıtım konusunda temel bilgiye ihtiyacınız varsa Python class ve nesne ile kalıtım ve super() yazılarına bakabilirsiniz.
add_note() ile hataya not eklemek
Bazen hatanın oluştuğu yer, ona bağlam katabilecek bilgiye sahip değildir. Örneğin int() geçersiz bir metinle karşılaştığını bilir ama bu metnin dosyanın kaçıncı satırında olduğunu bilmez. Python 3.11 ile gelen add_note(not) metodu, yakaladığınız istisnaya sonradan metin eklemenizi sağlar. Notlar __notes__ listesinde saklanır ve standart traceback'te istisna mesajının hemen altında, eklendikleri sırayla gösterilir:
def satir_isle(no, metin):
try:
return int(metin)
except ValueError as e:
e.add_note(f"satır {no} işlenirken oluştu")
e.add_note("dosya: sayilar.txt")
raise
try:
satir_isle(3, "x")
except ValueError as e:
print(e)
print(e.__notes__)
# çıktı: invalid literal for int() with base 10: 'x'
# çıktı: ['satır 3 işlenirken oluştu', 'dosya: sayilar.txt']Not yalnızca metin olabilir; e.add_note(5) çağrısı TypeError: add_note() argument must be str, not int hatası verir. Bu teknik, bir döngüde biriktirdiğiniz hataları sonradan bir grup hâlinde fırlatırken her birine "hangi adımda oluştu" bilgisini eklemek için de kullanılır.
ExceptionGroup: birden fazla hatayı birlikte bildirmek
Normalde bir işlem ilk hatada durur. Ama bir formdaki tüm alanları doğrularken ya da birden fazla görevi paralel çalıştırırken, hepsini toplayıp tek seferde bildirmek daha kullanışlıdır. Python 3.11 ile gelen yerleşik ExceptionGroup(mesaj, istisnalar) bir istisna listesini sarar ve kendisi de bir istisnadır. Listedeki öğeler sınıf değil örnek olmalıdır; ayrıca ExceptionGroup yalnızca Exception alt sınıflarını kabul eder:
try:
ExceptionGroup("g", [KeyboardInterrupt()])
except TypeError as e:
print(e)
# çıktı: Cannot nest BaseExceptions in an ExceptionGroupKeyboardInterrupt gibi BaseException türlerini sarmanız gerekiyorsa BaseExceptionGroup kullanılır. Belgelere göre BaseExceptionGroup yapıcısı, içindeki tüm öğeler Exception türündeyse kendiliğinden bir ExceptionGroup döndürür. Aşağıdaki doğrulama fonksiyonu tüm sorunları toplar ve grup hâlinde fırlatır; except* blokları da grubu türlere göre parçalar:
def kayit_dogrula(kayit):
hatalar = []
if not kayit.get("ad"):
hatalar.append(ValueError("ad boş"))
if not isinstance(kayit.get("yas"), int):
hatalar.append(TypeError("yaş tam sayı değil"))
if "@" not in kayit.get("eposta", ""):
hatalar.append(ValueError("e-posta geçersiz"))
if hatalar:
raise ExceptionGroup("kayıt geçersiz", hatalar)
try:
kayit_dogrula({"ad": "", "yas": "yirmi", "eposta": "ali"})
except* ValueError as grup:
print("Değer hataları:", [str(e) for e in grup.exceptions])
except* TypeError as grup:
print("Tip hataları:", [str(e) for e in grup.exceptions])
# çıktı: Değer hataları: ['ad boş', 'e-posta geçersiz']
# çıktı: Tip hataları: ['yaş tam sayı değil']except* normal except'ten farklı çalışır. Her except* bloğu grubu eşleşen ve eşleşmeyen iki alt gruba ayırır; eşleşen alt grup boş değilse blok çalışır ve kalan kısım bir sonraki except*'e geçer. Yani birden fazla except* bloğu aynı anda çalışabilir. Bloğa bağlanan değişken her zaman bir gruptur, tek bir istisna değildir; içindeki öğelere exceptions demeti ile ulaşırsınız. Hiçbir bloğun eşleşmediği hatalar yeni bir grup olarak yukarı fırlatılmaya devam eder.
Dil referansındaki bazı kurallar da bilinmelidir. Aynı try deyiminde except ile except* birlikte kullanılamaz. except* için tür yazmak zorunludur, except*: sözdizimi hatasıdır. except* ExceptionGroup gibi bir grup türüyle eşleştirmek anlamı belirsiz olduğu için TypeError verir. break, continue ve return bir except* bloğunda yer alamaz. Son olarak, try bloğundan grup olmayan tek bir istisna çıkar ve bir except* ile eşleşirse Python onu boş mesajlı bir gruba sarar:
try:
raise BlockingIOError
except* BlockingIOError as e:
print(repr(e))
# çıktı: ExceptionGroup('', (BlockingIOError(),))Grubu düz bir except Exception ile de yakalayabilirsiniz; bu durumda grubun tamamı tek bir nesne olarak gelir. Grubu elle parçalamak için subgroup(koşul) ve split(koşul) metotları vardır. Koşul bir tür, bir tür demeti ya da Python 3.13 ile birlikte her istisna için doğru veya yanlış döndüren herhangi bir çağrılabilir olabilir. split() eşleşen ve kalan kısmı bir çift olarak döndürür, boş kalan taraf None olur:
grup = ExceptionGroup("kayıt geçersiz",
[ValueError("a"), TypeError("b"), ValueError("c")])
print(grup)
eslesen, kalan = grup.split(ValueError)
print(repr(eslesen))
print(repr(kalan))
print(grup.subgroup(KeyError))
print(grup.subgroup(lambda e: str(e) == "b"))
# çıktı: kayıt geçersiz (3 sub-exceptions)
# çıktı: ExceptionGroup('kayıt geçersiz', [ValueError('a'), ValueError('c')])
# çıktı: ExceptionGroup('kayıt geçersiz', [TypeError('b')])
# çıktı: None
# çıktı: kayıt geçersiz (1 sub-exception)Eşzamanlı programlamada asyncio.TaskGroup başarısız görevlerin hatalarını bu yapıyla bildirir; ayrıntılar için Python asyncio yazısına bakabilirsiniz.
Sürüm notları
Bu yazıdaki özelliklerin bir kısmı yakın sürümlerde geldi. add_note(), __notes__, ExceptionGroup ve except* Python 3.11 ile eklendi. Python 3.13, subgroup() ve split() metotlarının koşul olarak herhangi bir çağrılabilir kabul etmesini sağladı. Python 3.14 ile except ve except* satırlarında as kullanılmadığında parantezsiz çoklu tür yazımı geldi. Python 3.15 ise yeni lazy import özelliğine bağlı olarak ImportError'ın alt sınıfı olan ImportCycleError'ı ekledi; bu hata, ertelenmiş bir içe aktarma doğrudan ya da dolaylı olarak kendisini içe aktarmaya çalıştığında oluşur. Aynı sürümde ImportError ve ModuleNotFoundError nesnelerinin repr() çıktısı, anahtar kelimeyle verilen name ve path değerlerini de göstermeye başladı:
print(repr(ImportError("modül yok", name="eklenti", path="/opt/eklenti.py")))
# çıktı: ImportError('modül yok', name='eklenti', path='/opt/eklenti.py')Python 3.15 ile gelen diğer değişikliklerin listesi Python 3.15 yenilikleri yazısındadır.
Sık yapılan hatalar

- Genel Exception fırlatmak:
raise Exception("hata")çağıranın hatayı türüne göre ayırmasını imkânsız kılar. Uygun yerleşik türü ya da kendi alt sınıfınızı kullanın. - BaseException'dan türetmek: Böyle bir sınıf
except Exceptionile yakalanmaz veSystemExitgibi davranır. Kendi sınıflarınızıException'dan türetin. - super().__init__() çağırmamak:
str(e)beklediğiniz mesaj yerine ham argüman demetini gösterir. - Dönüştürürken from kullanmamak: Traceback hatanın kasıtlı bir dönüştürme değil, ikinci bir kaza olduğunu söyler; okuyan kişi yanlış yöne bakar.
- from None ile bilgiyi gereksiz gizlemek: Alt katmandaki hata çoğu zaman sorunun kökünü gösterir. Gizlemeyi yalnızca eski hata kullanıcı için anlamsızsa tercih edin.
- ExceptionGroup'a sınıf vermek: Liste öğeleri
ValueErrordeğil,ValueError("...")gibi örnekler olmalıdır. - except* değişkenini tek istisna sanmak:
except* ValueError as eiçindekiebir gruptur; öğeleree.exceptionsile ulaşın.
Sık Sorulan Sorular
raise ile raise e arasında fark var mı?
İkisi de aynı istisnayı yeniden fırlatır. Argümansız raise yalnızca bir except bloğunun içinde (etkin istisna varken) çalışır ve niyeti en açık anlatan biçimdir. Etkin istisna yokken kullanılırsa RuntimeError: No active exception to reraise alırsınız.
Özel istisna sınıfına neden Error eki koyuluyor?
Bu bir zorunluluk değil, gelenektir. Belgeler çoğu istisnanın standart istisnalar gibi "Error" ile biten isimler aldığını belirtir; bu, sınıfın bir hata türü olduğunu ilk bakışta anlatır.
raise from None ne işe yarar?
Yeni istisnanın traceback'inde eski istisnanın gösterilmesini engeller. Eski istisna silinmez, __context__ özelliğinde durur. Bir iç ayrıntıyı (örneğin bir sözlük anahtarının yokluğunu) kullanıcıya daha anlamlı bir hatayla sunarken kullanılır.
except* ne zaman kullanılır?
Yalnızca ExceptionGroup ile çalışırken: birden fazla doğrulama hatasını toplayan kodlarda ya da paralel görevlerin hatalarını gruplayan asyncio.TaskGroup gibi araçlarda. Tek tek fırlatılan sıradan istisnalar için normal except yeterlidir.
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 try except · Python sık hata mesajları · Python class ve nesne · Python kalıtım ve super() · Python asyncio · Python unittest