---
title: 'Özel İstisnalar'
source: 'https://academia.sh/tr/kurslar/python-temelleri/ozel-istisnalar'
course: 'Python Temelleri'
language: tr
updated: '2026-08-17T18:10:27+00:00'
license: 'CC BY-SA 4.0'
---

# Özel İstisnalar

Özel bir istisna sınıfının hiyerarşideki yeri onu kimin yakalayacağını belirler; beş sınıf ile altı yakalama 30 çiftin 13'ünde tutar, alan kökü tek adla yedi hatayı ele alır, ödünç alınan taban yalnız üçünü tutar ve klozu geniş yazmak dar klozu sıfır kez çalıştırır.

Önceki dersin bütün sınıfları hazır gelenlerdi. `KeyError`, `ValueError`, `FileNotFoundError` —
soyağaçları önceden kurulmuştu ve ölçüm onları ortak atalarına göre gruplayabildi. Bir ara
sınıfı yakalamanın altındaki bütün yaprakları yakaladığını gördük; yerleştirmeyi yapan başkasıydı.

Peki fırlatılan şey **alanın kendi hatasıysa**? Ölçüm aygıtının kabul aralığı dışında değer
bildirmesi, bir kayıtta zorunlu alanın bulunmaması, sayıya çevrilemeyen bir ham veri. Bunlar
için bir sınıf yazmak gerekir ve o sınıfı soyağacının **neresine astığımız** bir üslup tercihi
değildir: yeri, onu **kimin yakalayacağını** belirler. Bu dersin sorusu budur.

## Alanın Kendi Hatası Bir Tiptir

Bir hatayı ileti dizgisiyle anlatmak ile tiple anlatmak arasında ölçülebilir bir fark vardır.
İleti bir dizgidir; onu okuyan kod dizgiyi ayrıştırmak zorundadır ve ileti değişince ayrıştırma
bozulur. Tip ise `except` deyiminin doğrudan sınadığı şeydir ve sınama `isinstance` ile yapılır.

Özel istisna yazmak bir sınıf tanımından ibarettir. Sınıf, taşıdığı alan verisini
**öznitelik** olarak tutabilir — hangi alan, hangi değer — ve yakalayan kod bunu ileti
ayrıştırmadan okur.

Asıl karar tabandır. Alan hataları için bir **kök** sınıf tanımlanır ve somut hatalar ondan
iner; böylece tek bir `except` satırı hepsini kapsar. Bunun yanında somut sınıf, anlamına
uyan yerleşik bir sınıftan **da** inebilir: kapsam dışı değer bir `ValueError`'dır, eksik alan
bir `KeyError`'dır. İki tabanlı bu yerleştirme bir kolaylık gibi görünür ve bedeli ölçülür.

## Kökün Seçilmesi

Ölçüm dört alan sınıfı kurar. `OlcumHatasi` köktür ve doğrudan `Exception`'dan iner.
`KapsamDisiDeger` kökten **ve** `ValueError`'dan, `EksikAlan` kökten **ve** `KeyError`'dan
iner. `BozukKayit` yalnız kökten iner. Beşinci sınıf `AygitHatasi` kökten inmez — ayrı bir
ağaçtır ve kökün kapsamadığını göstermek için vardır.

Yerleştirmenin kök tarafı bir ağaç olarak şöyle görünür; ödünç tabanlar ağacın öbür
dallarında durdukları için satır sonuna not düşülmüştür:

```text
# ogretilen dokum , calistirilmamistir

Exception
├── OlcumHatasi              alanın kökü
│   ├── KapsamDisiDeger      ayrıca ValueError
│   ├── EksikAlan            ayrıca KeyError
│   └── BozukKayit           yalnız kökten
└── AygitHatasi              kökten inmez
```

Ölçümün varsayımları:

- **HD10** — Soyağacı zinciri sınıfın kendi çözüm sırasından okunur, elle yazılmaz; `object`
  sayılmaz çünkü her sınıf ondan iner.
- **HD11** — Bir çiftin "tuttuğu", yakalama sınıfının fırlatılan sınıfı `issubclass` ile
  kapsaması demektir; `except` deyiminin yaptığı sınama budur ve sınıf düzeyinde önceden
  bilinebilir.
- **HD12** — "Yabancı yakalama", alandan habersiz bir kodun kendi işi için yazdığı
  `except ValueError` ya da `except KeyError` klozudur; alan sınıfının oraya düşmesi
  **sızmadır**.
- **HD13** — Sınıflar yalnız yerleştirme için tanımlanır; hiçbiri davranış eklemez, bu yüzden
  ölçülen tek şey **hiyerarşideki yerdir**.

```python
"""Ozel istisna: sinifin hiyerarsideki yeri yakalama davranisini belirler."""


class OlcumHatasi(Exception):
    """Alanin koku: olcum kaydini isleyen her hata buradan iner."""

    def __init__(self, alan, deger=None):
        super().__init__(alan)
        self.alan = alan
        self.deger = deger

    def __str__(self):
        return f"{type(self).__name__}(alan={self.alan!r}, deger={self.deger!r})"


class KapsamDisiDeger(OlcumHatasi, ValueError):
    """Deger okundu ama alanin kabul araligi disinda."""


class EksikAlan(OlcumHatasi, KeyError):
    """Kayitta zorunlu alan yok."""


class BozukKayit(OlcumHatasi):
    """Kayit sayiya cevrilemedi; alanin disinda bir karsiligi yok."""


class AygitHatasi(Exception):
    """Alan kokunden inmez: ayri bir agac."""


SINIFLAR = (("OlcumHatasi", OlcumHatasi), ("KapsamDisiDeger", KapsamDisiDeger),
            ("EksikAlan", EksikAlan), ("BozukKayit", BozukKayit),
            ("AygitHatasi", AygitHatasi))

YAKALAMALAR = (("OlcumHatasi", OlcumHatasi), ("KapsamDisiDeger", KapsamDisiDeger),
               ("ValueError", ValueError), ("KeyError", KeyError),
               ("LookupError", LookupError), ("Exception", Exception))

print("sınıf             hiyerarşideki yeri")
for ad, s in SINIFLAR:
    zincir = " < ".join(k.__name__ for k in s.__mro__ if k is not object)
    print(f"  {ad:16s}{zincir}")

print()
baslik = "".join(f"{y:<17s}" for y, _ in YAKALAMALAR)
print(f"{'fırlatılan':<18s}{baslik}")
for ad, s in SINIFLAR:
    hucre = "".join(f"{('tutar' if issubclass(s, ys) else '-'):<17s}"
                    for _, ys in YAKALAMALAR)
    print(f"  {ad:<16s}{hucre}")

print()
tutan = sum(issubclass(s, ys) for _, s in SINIFLAR for _, ys in YAKALAMALAR)
print(f"sınıf {len(SINIFLAR)} | yakalama {len(YAKALAMALAR)} | "
      f"çift {len(SINIFLAR) * len(YAKALAMALAR)} | tutan çift {tutan}")
kok = sum(issubclass(s, OlcumHatasi) for _, s in SINIFLAR)
sizan = sum(issubclass(s, (ValueError, KeyError)) for _, s in SINIFLAR)
print(f"alan kökünden inen {kok} | yabancı yakalamaya sızan {sizan}")
```

```
sınıf             hiyerarşideki yeri
  OlcumHatasi     OlcumHatasi < Exception < BaseException
  KapsamDisiDeger KapsamDisiDeger < OlcumHatasi < ValueError < Exception < BaseException
  EksikAlan       EksikAlan < OlcumHatasi < KeyError < LookupError < Exception < BaseException
  BozukKayit      BozukKayit < OlcumHatasi < Exception < BaseException
  AygitHatasi     AygitHatasi < Exception < BaseException

fırlatılan        OlcumHatasi      KapsamDisiDeger  ValueError       KeyError         LookupError      Exception        
  OlcumHatasi     tutar            -                -                -                -                tutar            
  KapsamDisiDeger tutar            tutar            tutar            -                -                tutar            
  EksikAlan       tutar            -                -                tutar            tutar            tutar            
  BozukKayit      tutar            -                -                -                -                tutar            
  AygitHatasi     -                -                -                -                -                tutar            

sınıf 5 | yakalama 6 | çift 30 | tutan çift 13
alan kökünden inen 4 | yabancı yakalamaya sızan 2
```

## Ödünç Alınan Tabanın Ödettiği

Üst tablo yerleştirmenin sonucunu veriyor. `OlcumHatasi` sütunu **4** satırda tutuyor —
kökten inen dört sınıfın hepsi. `AygitHatasi` satırı boş; kök onu kapsamıyor, çünkü ondan
inmiyor. Alanın kökünü yazmak, alanın **sınırını** yazmaktır.

İlginç olan `ValueError` ve `KeyError` sütunlarıdır. Her biri **1** satırda tutuyor ve o
satırlar ödünç taban alan sınıflardır. `EksikAlan` üstelik `LookupError` sütununda da tutuyor:
`KeyError`'dan indiği için onun atasını da miras almış oldu. Bir tek yerleştirme kararı, sınıfı
**üç ayrı yabancı klozun** kapsamına soktu.

Alt satır bunu sayıyor: dört alan sınıfının **2**'si yabancı yakalamaya sızıyor. Bu, alandan
habersiz bir kodun `int()` çağrısı için yazdığı `except ValueError` klozunun `KapsamDisiDeger`
nesnesini de yakalayacağı anlamına gelir — ölçüm hatası, bir dönüşüm hatası sanılarak ele
alınır. Ödünç taban, sınıfa **anlam** kazandırır ve karşılığında **kapsam** verir.

Otuz çiftin **13**'ü tutuyor. Bunların **5**'i `Exception` sütunundan geliyor; her sınıf ondan
iner ve o sütun ayırt edici değildir. Ayırt eden sütunlar kök ile ödünç tabanlardır.

## Yakalama Biçimleri ve Kloz Sırası

İkinci ölçüm sınıfları çalıştırır. On iki ölçüm kaydı işlenir; kayıtların bir bölümü geçerli,
bir bölümü alan hatası üretir, biri de **program kusuru** üretir — katsayı alanı yanlış tiple
yazılmıştır ve çarpma `TypeError` fırlatır. Dört yakalama biçimi denenir ve her biri için üç
sayı okunur: kaç ad yazıldı, kaç alan hatası ele alındı, kaç program kusuru yutuldu.

- **HD14** — On iki kaydın her biri gerçekten işlenir; sonuç sınıfı elle etiketlenmez,
  fırlatılan nesneden okunur.
- **HD15** — Kâhin kurgudur: hangi kaydın alan hatası, hangisinin program kusuru olduğu
  kayıtların nasıl kurulduğundan bilinir.
- **HD16** — "Ele alınan", yakalanan nesnenin `OlcumHatasi` örneği olmasıdır; "yutulan kusur"
  yakalanan ama alan kökünden inmeyen nesnedir.
- **HD17** — "Dışarı çıkan", biçimin klozuna düşmeyip sonraki kloza geçen kayıt sayısıdır.
- **HD18** — Kloz sırası ölçümünde iki blok **aynı** iki sınıfı yazar, yalnız sırayı
  değiştirir; başka hiçbir fark yoktur.

```python
"""Yakalama bicimleri: kac ad yazilir, kac alan hatasi ele alinir, kac kusur yutulur."""


class OlcumHatasi(Exception):
    def __init__(self, alan, deger=None):
        super().__init__(alan)
        self.alan = alan
        self.deger = deger

    def __str__(self):
        return f"{type(self).__name__}(alan={self.alan!r}, deger={self.deger!r})"


class KapsamDisiDeger(OlcumHatasi, ValueError):
    pass


class EksikAlan(OlcumHatasi, KeyError):
    pass


class BozukKayit(OlcumHatasi):
    pass


ALT_SINIR, UST_SINIR = -50.0, 60.0

KAYITLAR = (
    {"deger": "12.5", "katsayi": 2},
    {"deger": "-3", "katsayi": 1},
    {"deger": "99", "katsayi": 1},
    {"katsayi": 1},
    {"deger": "kuzey", "katsayi": 1},
    {"deger": "0", "katsayi": 3},
    {"deger": "-80", "katsayi": 1},
    {"deger": None, "katsayi": 1},
    {"deger": "45", "katsayi": "2"},
    {"katsayi": 5},
    {"deger": "60", "katsayi": 1},
    {"deger": "60.1", "katsayi": 1},
)


def isle(kayit):
    if "deger" not in kayit:
        raise EksikAlan("deger")
    ham = kayit["deger"]
    try:
        sayi = float(ham)
    except (TypeError, ValueError):
        raise BozukKayit("deger", ham) from None
    if not ALT_SINIR <= sayi <= UST_SINIR:
        raise KapsamDisiDeger("deger", sayi)
    return kayit["katsayi"] * sayi


def sinif_adi(kayit):
    try:
        isle(kayit)
    except BaseException as e:
        return type(e).__name__
    return "-"


sonuc = [sinif_adi(k) for k in KAYITLAR]
print("kayıt sonucu:", ", ".join(f"{i}:{s}" for i, s in enumerate(sonuc, 1)))
alan = sum(s in ("KapsamDisiDeger", "EksikAlan", "BozukKayit") for s in sonuc)
kusur = sum(s == "TypeError" for s in sonuc)
print(f"kayıt {len(KAYITLAR)} | başarılı {sonuc.count('-')} | "
      f"alan hatası {alan} | program kusuru {kusur}")

BICIMLER = (
    ("üç ad", (KapsamDisiDeger, EksikAlan, BozukKayit), 3),
    ("alan kökü", (OlcumHatasi,), 1),
    ("ödünç taban", (ValueError,), 1),
    ("geniş", (Exception,), 1),
)

print()
print(f"{'yakalama biçimi':<17s}{'yazılan ad':>11s}{'ele alınan':>12s}"
      f"{'yutulan kusur':>15s}{'dışarı çıkan':>14s}")
for ad, sinif, adet in BICIMLER:
    ele = yutulan = disari = 0
    for k in KAYITLAR:
        try:
            isle(k)
        except sinif as e:
            if isinstance(e, OlcumHatasi):
                ele += 1
            else:
                yutulan += 1
        except Exception:
            disari += 1
    print(f"  {ad:<15s}{adet:>11d}{ele:>12d}{yutulan:>15d}{disari:>14d}")

print()
kok_once = {"OlcumHatasi": 0, "KapsamDisiDeger": 0}
for k in KAYITLAR:
    try:
        isle(k)
    except OlcumHatasi:
        kok_once["OlcumHatasi"] += 1
    except KapsamDisiDeger:
        kok_once["KapsamDisiDeger"] += 1
    except Exception:
        pass

dar_once = {"KapsamDisiDeger": 0, "OlcumHatasi": 0}
for k in KAYITLAR:
    try:
        isle(k)
    except KapsamDisiDeger:
        dar_once["KapsamDisiDeger"] += 1
    except OlcumHatasi:
        dar_once["OlcumHatasi"] += 1
    except Exception:
        pass

print("kloz sırası     birinci kloz              ikinci kloz")
print(f"  kök önce      OlcumHatasi {kok_once['OlcumHatasi']:<13d}"
      f" KapsamDisiDeger {kok_once['KapsamDisiDeger']}")
print(f"  dar önce      KapsamDisiDeger {dar_once['KapsamDisiDeger']:<9d}"
      f" OlcumHatasi {dar_once['OlcumHatasi']}")

print()
print("özniteliğiyle bildirilen alan hataları:")
for i, k in enumerate(KAYITLAR, 1):
    try:
        isle(k)
    except OlcumHatasi as e:
        print(f"  kayıt {i:2d}: {e} -> alan {e.alan!r}, değer {e.deger!r}")
    except Exception:
        pass
```

```
kayıt sonucu: 1:-, 2:-, 3:KapsamDisiDeger, 4:EksikAlan, 5:BozukKayit, 6:-, 7:KapsamDisiDeger, 8:BozukKayit, 9:TypeError, 10:EksikAlan, 11:-, 12:KapsamDisiDeger
kayıt 12 | başarılı 4 | alan hatası 7 | program kusuru 1

yakalama biçimi   yazılan ad  ele alınan  yutulan kusur  dışarı çıkan
  üç ad                    3           7              0             1
  alan kökü                1           7              0             1
  ödünç taban              1           3              0             5
  geniş                    1           7              1             0

kloz sırası     birinci kloz              ikinci kloz
  kök önce      OlcumHatasi 7             KapsamDisiDeger 0
  dar önce      KapsamDisiDeger 3         OlcumHatasi 4

özniteliğiyle bildirilen alan hataları:
  kayıt  3: KapsamDisiDeger(alan='deger', deger=99.0) -> alan 'deger', değer 99.0
  kayıt  4: EksikAlan(alan='deger', deger=None) -> alan 'deger', değer None
  kayıt  5: BozukKayit(alan='deger', deger='kuzey') -> alan 'deger', değer 'kuzey'
  kayıt  7: KapsamDisiDeger(alan='deger', deger=-80.0) -> alan 'deger', değer -80.0
  kayıt  8: BozukKayit(alan='deger', deger=None) -> alan 'deger', değer None
  kayıt 10: EksikAlan(alan='deger', deger=None) -> alan 'deger', değer None
  kayıt 12: KapsamDisiDeger(alan='deger', deger=60.1) -> alan 'deger', değer 60.1
```

## Sayıların Okunması

On iki kaydın **4**'ü başarılı, **7**'si alan hatası, **1**'i program kusuru. Dört biçim bu
dağılımı ayrı ayrı bölüyor.

**Üç ad** ve **alan kökü** aynı sonucu veriyor: **7** ele alınan, **0** yutulan kusur, **1**
dışarı çıkan. İkisi ayırt edilemez — ama biri **3** ad yazıyor, öbürü **1**. Kökün ödediği şey
tam olarak budur: alan büyüdükçe yeni somut sınıflar kökten inecek ve `except OlcumHatasi`
yazan hiçbir kloz değişmeyecek. Üç adı sayan kloz her yeni sınıfta güncellenmek zorundadır.

**Ödünç taban** yalnız **3** hatayı ele alıyor ve **5** kaydı dışarı bırakıyor.
`except ValueError` yazmak `KapsamDisiDeger`'i tutar, ama `EksikAlan` ile `BozukKayit` ona
düşmez. Ödünç
taban bir **grup adı değildir**; her somut sınıfın kendi ödünç tabanı ayrıdır ve aralarında
ortaklık yoktur. Alanın grup adı yalnız köktür.

**Geniş** yakalama **0** kaydı dışarı bırakıyor — ve **1** program kusurunu yutuyor. Katsayı
alanı yanlış tiple yazılmış kayıt, bir alan hatası gibi ele alınıyor. Önceki dersin bulgusu
burada somutlaşıyor: kusuru gizleyen şey kusurun kendisi değil, onu kapsayan klozdur.

**Kloz sırası** tablosu en keskin sayıyı veriyor. Kök önce yazıldığında `OlcumHatasi` klozu
**7** kez, `KapsamDisiDeger` klozu **0** kez çalışıyor. İkinci kloz yazılmıştır, sözdizimi
doğrudur, hiçbir uyarı vermez ve **hiç çalışmaz** — çünkü ilk eşleşen kloz kazanır ve kök
zaten hepsini kapsar. Sıra çevrildiğinde dar kloz **3**, kök klozu **4** alıyor; toplam yine
**7**. Yakalama klozları **dardan geniş'e** yazılır; tersi sessizce ölü kod üretir.

Son döküm özniteliğin ne kazandırdığını gösteriyor. Her satır alan adını ve değeri **ileti
ayrıştırmadan** veriyor; `-80.0` ile `60.1` arasındaki farkı okuyan kod, dizgiyle değil
sayıyla çalışıyor.

## Sınırda Çeviri

Kökün ikinci işi tabloda görünmez ama ölçümün kodunda durur. `isle` işlevi ham değeri
`float` ile çevirmeye çalışır ve bu çağrı, verinin biçimine göre `ValueError` ya da
`TypeError` fırlatabilir. Bu iki sınıfın hiçbiri alanın sınıfı değildir; ikisi de dönüşüm
katmanının sınıfıdır. Kod onları yakalar ve yerine `BozukKayit` fırlatır:

`except (TypeError, ValueError): raise BozukKayit("deger", ham) from None`

Bu üç satır bir **sınır çevirisidir**. İçeride ne olduğunu bilen kod, dışarıya alanın diliyle
konuşur. Çıktının sekizinci kaydı bunun sonucudur: `deger` alanı sayı değil, hiç değer
değildir ve `float` ondan `TypeError` üretir — ama dışarı çıkan sınıf `BozukKayit`'tir ve
`except OlcumHatasi` klozu onu tutar. Çeviri yapılmasaydı o kayıt, önceki bölümde "program
kusuru" diye ayrılan kayıtla aynı sınıfa düşer ve ayırt edilemezdi.

Önceki dersin zincir ölçümü buradaki `from None`'ın bedelini de veriyor: bağlam bastırıldığı
için dışarıdan bakan biri `TypeError`'ı göremez. Bu bilinçli bir seçimdir — alan katmanının
dışına dönüşüm ayrıntısı sızmaz. Ayrıntının tutulması istenirse `from` yazılır ve zincir
ikiye çıkar; karar, sınırın hangi tarafına bilgi verileceğidir.

Çevirinin sınırı da vardır. Katsayı alanındaki `TypeError` çevrilmedi, çünkü o hata dönüşüm
katmanından değil, kaydı **kuran** koddan geliyor. Bir program kusurunu alan sınıfına
çevirmek, onu ele alınabilir bir şey gibi göstermek olurdu.

## Özet

- Özel istisna sınıfının hiyerarşideki yeri bir üslup tercihi değildir; onu **kimin
  yakalayacağını** belirler.
- Beş sınıf ile altı yakalama **30** çift üretir, **13**'ü tutar; alan kökü **4** sınıfı
  kapsarken kökten inmeyen sınıfı kapsamaz.
- Ödünç taban sınıfa anlam kazandırır ve karşılığında kapsam verir: dört alan sınıfının
  **2**'si yabancı `except ValueError` ya da `except KeyError` klozuna sızar.
- Alan kökü **1** adla **7** hatayı ele alır; üç adı sayan kloz aynı sonucu **3** adla verir,
  ödünç taban yalnız **3** hatayı tutar, geniş kloz **1** program kusurunu yutar.
- Kloz sırası dardan geniş'e olmalıdır: kök önce yazıldığında dar kloz **0** kez çalışır ve
  sessizce ölü kod hâline gelir.

## Sonraki Adım

Bu dersin on iki kaydı bellekte hazır duruyordu; `deger` alanı zaten bir dizgiydi ve tek
sorun onu sayıya çevirmekti. Kayıtlar bir dosyadan geldiğinde araya bir katman daha girer:
dosyada duran şey ne dizgidir ne sayı, **bayttır**. Baytın dizgiye çevrilmesi bir kodlama
kararıdır ve o karar yanlışsa hata alan katmanına hiç ulaşmaz — daha okuma sırasında ortaya
çıkar. Sonraki ders aynı baytların metin kipiyle ikili kipte ne verdiğini ve yanlış kodlamanın
tam olarak hangi noktada istisna ürettiğini ölçer.
