---
title: 'Bağlam Yöneticileri'
source: 'https://academia.sh/tr/kurslar/python-temelleri/baglam-yoneticileri'
course: 'Python Temelleri'
language: tr
updated: '2026-08-17T18:10:27+00:00'
license: 'CC BY-SA 4.0'
---

# Bağlam Yöneticileri

with bloğu girerken __enter__, çıkarken __exit__ çağırır; istisnasız blokta 2 çağrı olur, istisnalı blokta 3 ve __exit__ istisnayı görür — dönüş değeri True olduğunda çağrı yine 2'ye iner çünkü istisna dışarı çıkmaz.

Önceki dersin bütün dosya örnekleri `with` bloğu içinde açıldı ve blok bitince dosya kapandı.
Ölçüm sonunda geçici dizin bile silinebildi, çünkü açık kalan bir tutamak kalmamıştı. `with`
şimdiye kadar bir alışkanlık olarak kullanıldı.

Oysa `with` de bir sözdizimdir ve bu kursun kuralı şudur: her sözdizim bir **kısaltmadır** ve
adı belli bir **özel yöntemi** çağırır. Bu ders o protokolü ölçer. Kaynak yaşam süresi ve
kaynağın serbest bırakılması İşletim Sistemi Kavramları kursunda kuruldu; orada **kavram**
kuruldu, burada Python'un onu hangi protokolle gerçekleştirdiği ölçülüyor: `with` girerken
`__enter__`, çıkarken `__exit__` çağırır — ve blok içinde **bir istisna oluştuğunda**
`__exit__` **yine çağrılır**.

## Protokol İki Yöntemden İbarettir

Bir nesnenin `with` deyiminde kullanılabilmesi için tek koşul vardır: `__enter__` ve
`__exit__` tanımlamak. Tipinin adı, hangi sınıftan indiği, ne iş yaptığı önemsizdir. Protokol
yoksa sözdizim de yoktur; protokol varsa nesne `with` içine girer.

`__enter__` argümansızdır ve döndürdüğü değer `as` ile bağlanan addır. Kaynağın kendisini
döndürmek zorunda değildir; başka bir nesne de döndürebilir.

`__exit__` **üç argüman** alır: oluşan istisnanın **türü**, **nesnesi** ve **izi**. Blok
istisnasız bittiyse üçü de boştur. Ve `__exit__` bir değer **döndürür**; bu dönüş değeri
istisnanın yutulup yutulmayacağına karar verir. Doğru sayılan bir dönüş istisnayı yutar,
yanlış sayılan bir dönüş onu dışarı bırakır.

Ortak kurgunun `Izci` nesnesi bu iki yöntemi tanımlar ve `__exit__` yöntemi `False` döndürür —
yani **yutmaz**. Ölçüm buna bir kardeş ekler: `Yutan`, yalnız dönüş değerini değiştirir.

## Ölçümün Kurgusu

Üç sözdizim biçimi çalıştırılır: istisnasız `with`, istisnalı `with`, ve istisnalı `with` ama
yutan bir nesneyle. Her biçimde hangi özel yöntemin kaç kez çağrıldığı sayılır. Ölçüm kaydı
istisnayı da bir olay olarak yazar: bir biçim istisnayla bittiyse kayda `!` önekiyle sınıf
adı düşer.

- **HD31** — `Izci`'nin `__enter__` ve `__exit__` yöntemleri ortak kurgudan değiştirilmeden
  alınır; `__exit__` yine `False` döndürür.
- **HD32** — `Yutan` yalnız `__exit__` dönüşünü değiştirir ve üst sınıfın kaydını **bozmadan**
  çağırır; kayıt düzeni ortak kurgunun düzenidir.
- **HD33** — `Tanik` de üst sınıfın kaydını bozmaz; ek olarak `__exit__`'in gördüğü üçlüyü
  ayrı bir listeye yazar. `Izci`'nin davranışına dokunulmaz.
- **HD34** — Çağrı sayısı kayıttaki öğe sayısıdır; `!ValueError` da bir öğedir, çünkü ölçülen
  şey **biçimin ürettiği olay dizisidir**, yalnız yöntem çağrıları değil.
- **HD35** — Dönüş değeri sınaması `__exit__` doğrudan çağrılarak okunur; yutup yutmadığı ise
  gerçek bir `with` bloğuyla ayrıca sınanır. İki yol birbirini doğrular.

```python
"""Baglam yoneticisi protokolu: with girerken ve cikarken hangi yontem cagrilir."""

KAYIT = []
GORULEN = []


def kaydet(ad):
    KAYIT.append(ad)


class Izci:
    """Ortak tanimin cekirdegi; burada baglam parcasi kullaniliyor."""

    def __init__(self, ogeler=(1, 2, 3)):
        self.ogeler = list(ogeler)

    def __enter__(self):
        kaydet("__enter__")
        return self

    def __exit__(self, tur, deger, iz):
        kaydet("__exit__")
        return False          # istisnayi yutmaz


class Yutan(Izci):
    """__exit__ True dondurur: istisna disari cikmaz."""

    def __exit__(self, tur, deger, iz):
        super().__exit__(tur, deger, iz)
        return True


class Tanik(Izci):
    """__exit__'in gordugu ucluyu ayrica kaydeder; Izci'nin kaydini bozmaz."""

    def __init__(self, ad, yutar=False, ogeler=(1, 2, 3)):
        super().__init__(ogeler)
        self.ad = ad
        self.yutar = yutar

    def __exit__(self, tur, deger, iz):
        GORULEN.append((self.ad, tur.__name__ if tur else "-",
                        str(deger) if deger else "-", iz is not None))
        super().__exit__(tur, deger, iz)
        return self.yutar


def olc(islev):
    """Bir sozdizim bicimini calistirir ve cagrilan protokolleri dondurur."""
    KAYIT.clear()
    try:
        islev()
    except Exception as e:
        KAYIT.append(f"!{type(e).__name__}")
    return list(KAYIT)


def baglam():
    with Izci():
        pass


def baglam_istisnali():
    with Izci():
        raise ValueError("ornek")


def baglam_yutan():
    with Yutan():
        raise ValueError("ornek")


BICIMLER = (
    ("with n", baglam),
    ("with n + istisna", baglam_istisnali),
    ("with yutan + istisna", baglam_yutan),
)

print(f"{'sözdizim':<20s} {'çağrı':>6s}  protokol dizisi")
for ad, islev in BICIMLER:
    c = olc(islev)
    print(f"{ad:<20s} {len(c):6d}  {' '.join(c)}")

print()
print(f"{'sınıf':<10s}{'__exit__ dönüşü':<18s}istisna dışarı çıktı mı")
for ad, sinif in (("Izci", Izci), ("Yutan", Yutan)):
    n = sinif()
    donus = n.__exit__(ValueError, ValueError("ornek"), None)
    KAYIT.clear()
    try:
        with sinif():
            raise ValueError("ornek")
    except ValueError:
        cikti = "evet"
    else:
        cikti = "hayır"
    print(f"  {ad:<8s}{str(donus):<18s}{cikti}")

print()
GORULEN.clear()
with Tanik("istisnasız"):
    pass
try:
    with Tanik("istisnalı"):
        raise ValueError("ölçüm kapsam dışı")
except ValueError:
    pass
print(f"{'tanık':<14s}{'gördüğü tür':<14s}{'gördüğü değer':<22s}iz nesnesi var mı")
for ad, tur, deger, iz in GORULEN:
    print(f"  {ad:<12s}{tur:<14s}{deger:<22s}{'evet' if iz else 'hayır'}")
```

```
sözdizim              çağrı  protokol dizisi
with n                    2  __enter__ __exit__
with n + istisna          3  __enter__ __exit__ !ValueError
with yutan + istisna      2  __enter__ __exit__

sınıf     __exit__ dönüşü   istisna dışarı çıktı mı
  Izci    False             evet
  Yutan   True              hayır

tanık         gördüğü tür   gördüğü değer         iz nesnesi var mı
  istisnasız  -             -                     hayır
  istisnalı   ValueError    ölçüm kapsam dışı     evet
```

## Üç Satırın Söylediği

İlk iki satır ortak kurgunun `with` satırlarıdır. **`with n` iki çağrı üretir:** `__enter__`
ve `__exit__`. **`with n + istisna` üç olay üretir:** `__enter__`, `__exit__` ve
`!ValueError`. Aradaki farkı doğru okumak gerekir — fazladan olan **bir yöntem çağrısı
değildir**. Her iki biçimde de `__enter__` bir kez, `__exit__` bir kez çağrıldı. Fazladan olan
şey, biçimin **istisnayla bitmesidir**.

Bu, dersin merkez cümlesini veriyor: **istisna oluştuğunda `__exit__` yine çağrılır.** Blok
yarıda kesildi, kalan satırlar çalışmadı, denetim `with` deyiminden dışarı fırladı — ve
`__exit__` yine de çalıştı. Kaynağın kapanmasını sağlayan şey budur ve `with` yazmanın
gerekçesi de budur.

Üçüncü satır aynı istisnayı `Yutan` ile deniyor ve çağrı sayısı **2**'ye iniyor. Dizi yine
`__enter__ __exit__` — yöntem çağrıları değişmedi. Değişen tek şey, kayda `!ValueError`
düşmemesi: istisna **dışarı çıkmadı**. Orta tablo bunu doğruluyor. `Izci`'nin `__exit__`
dönüşü `False` ve istisna dışarı **çıktı**; `Yutan`'ın dönüşü `True` ve **çıkmadı**. Tek
satırlık bir dönüş değeri, üç olaylık bir biçimi iki olaya indirdi.

Son tablo `__exit__`'in **ne gördüğünü** veriyor. İstisnasız blokta üç argümanın da boş
olduğu görülüyor: tür `-`, değer `-`, iz **hayır**. İstisnalı blokta tür `ValueError`, değer
istisnanın iletisi ve iz nesnesi **var**. `__exit__` istisnayı yalnız yutup yutmamakla
kalmaz; **hangi istisna olduğunu bilir** ve kararını ona bakarak verebilir. Belli bir sınıfı
yutup ötekileri geçiren bir bağlam yöneticisi bu üçlüyle yazılır.

## İç İçe Bloklar ve try/finally

İkinci ölçüm iki soruyu birden yanıtlar. Birincisi: iki bağlam yöneticisi iç içe olduğunda
istisnayı hangisi görür ve içteki yutarsa dıştaki ne görür? İkincisi: `with` ile `try/finally`
arasındaki fark ölçülebilir mi?

- **HD36** — İç içe ölçümde dört birleşim denenir: dıştaki ve içteki yönetici ayrı ayrı yutan
  ya da yutmayan olur. Başka hiçbir şey değişmez.
- **HD37** — "Gördü" sütunu `__exit__`'e geçen tür argümanından okunur; boş geçildiyse `-`
  yazılır.
- **HD38** — Çıkış biçimi tablosunda dört yol denenir: normal bitiş, `return`, `break` ve
  istisna. `return` ve `break` için blok bir döngü ve bir işlev içine konur, çünkü ikisi de
  ancak orada anlamlıdır.
- **HD39** — `with` kolu ile `try/finally` kolu **aynı dört yolu** dener; aralarındaki tek
  fark, birinde bağlam yöneticisi, ötekinde bir `finally` bloğu olmasıdır.

```python
"""Ic ice with, cikis bicimleri ve try/finally ile karsilastirma."""

KAYIT = []
GORULEN = []


def kaydet(ad):
    KAYIT.append(ad)


class Izci:
    def __init__(self, ogeler=(1, 2, 3)):
        self.ogeler = list(ogeler)

    def __enter__(self):
        kaydet("__enter__")
        return self

    def __exit__(self, tur, deger, iz):
        kaydet("__exit__")
        return False


class Tanik(Izci):
    """Adiyla kaydeder ve yutup yutmayacagini kurulusta alir."""

    def __init__(self, ad, yutar=False, ogeler=(1, 2, 3)):
        super().__init__(ogeler)
        self.ad = ad
        self.yutar = yutar

    def __exit__(self, tur, deger, iz):
        GORULEN.append((self.ad, tur.__name__ if tur else "-"))
        super().__exit__(tur, deger, iz)
        return self.yutar


print(f"{'dış':<10s}{'iç':<9s}{'çağrı':>6s}  {'dizi':<40s}"
      f"{'dış gördü':<12s}{'iç gördü':<12s}istisna")
for dis_yutar in (False, True):
    for ic_yutar in (False, True):
        KAYIT.clear()
        GORULEN.clear()
        try:
            with Tanik("dış", dis_yutar), Tanik("iç", ic_yutar):
                raise ValueError("ölçüm kapsam dışı")
        except ValueError:
            sonuc = "çıktı"
        else:
            sonuc = "yutuldu"
        gorulen = dict(GORULEN)
        print(f"  {('yutar' if dis_yutar else 'yutmaz'):<8s}"
              f"{('yutar' if ic_yutar else 'yutmaz'):<9s}{len(KAYIT):6d}  "
              f"{' '.join(KAYIT):<40s}{gorulen['dış']:<12s}"
              f"{gorulen['iç']:<12s}{sonuc}")

print()
FINALLY = []


def with_ile(bicim):
    KAYIT.clear()
    GORULEN.clear()
    for _ in range(1):
        with Tanik("tek"):
            if bicim == "return":
                return
            if bicim == "break":
                break
            if bicim == "istisna":
                raise ValueError("ölçüm kapsam dışı")


def finally_ile(bicim):
    FINALLY.clear()
    for _ in range(1):
        try:
            if bicim == "return":
                return
            if bicim == "break":
                break
            if bicim == "istisna":
                raise ValueError("ölçüm kapsam dışı")
        finally:
            FINALLY.append("finally")


print(f"{'çıkış biçimi':<16s}{'__exit__ çağrıldı':<19s}{'finally çalıştı':<17s}"
      f"__exit__ istisnayı gördü")
for bicim in ("normal", "return", "break", "istisna"):
    try:
        with_ile(bicim)
    except ValueError:
        pass
    cagrildi = "evet" if "__exit__" in KAYIT else "hayır"
    gordu = "evet" if GORULEN and GORULEN[0][1] != "-" else "hayır"
    try:
        finally_ile(bicim)
    except ValueError:
        pass
    calisti = "evet" if FINALLY else "hayır"
    print(f"  {bicim:<14s}{cagrildi:<19s}{calisti:<17s}{gordu}")
```

```
dış       iç        çağrı  dizi                                    dış gördü   iç gördü    istisna
  yutmaz  yutmaz        4  __enter__ __enter__ __exit__ __exit__   ValueError  ValueError  çıktı
  yutmaz  yutar         4  __enter__ __enter__ __exit__ __exit__   -           ValueError  yutuldu
  yutar   yutmaz        4  __enter__ __enter__ __exit__ __exit__   ValueError  ValueError  yutuldu
  yutar   yutar         4  __enter__ __enter__ __exit__ __exit__   -           ValueError  yutuldu

çıkış biçimi    __exit__ çağrıldı  finally çalıştı  __exit__ istisnayı gördü
  normal        evet               evet             hayır
  return        evet               evet             hayır
  break         evet               evet             hayır
  istisna       evet               evet             evet
```

## Sayıların Okunması

İç içe tablonun **dört** satırında da çağrı sayısı **4** ve dizi aynı:
`__enter__ __enter__ __exit__ __exit__`. Giriş dıştan içe, çıkış içten dışa. Yutma kararı
**hiçbir çağrıyı iptal etmiyor** — dört yöntem dört birleşimde de çalışıyor.

Değişen şey `gördü` sütunlarıdır. İçteki yönetici **dört satırda da** `ValueError` görüyor;
istisna ona en yakın yerde oluşuyor ve önce ona geliyor. Dıştaki ise içteki **yutmadığında**
görüyor, yuttuğunda `-` alıyor. İkinci ve dördüncü satırlar bunu gösteriyor: içteki yuttuysa
dıştaki için ortada bir istisna kalmamıştır ve `__exit__` boş bir üçlüyle çağrılır.

Üçüncü satır tersini gösteriyor: içteki yutmadı, dıştaki gördü ve yuttu. İstisna dışarı
çıkmadı. Yutma yetkisi **yukarı doğru sırayla** dolaşır ve ilk yutan zinciri keser.

İkinci tablo `with` ile `try/finally` arasındaki ortak yanı ve farkı ayırıyor. Ortak yan
şudur: dört çıkış biçiminin **hepsinde** hem `__exit__` çağrılıyor hem `finally` çalışıyor —
blok hangi yolla biterse bitsin temizlik kodu çalışır. Bu yüzden `with` bir "hata yakalama"
aracı değildir; **çıkış aracıdır**
ve dört yolun dördünde de devrededir.

Fark son sütundadır. `__exit__` yalnız istisna biçiminde **istisnayı görüyor**; öbür üç
biçimde boş üçlüyle çağrılıyor. `finally` bloğu ise hiçbir biçimde bir üçlü almaz — çalıştığı
sırada hangi istisnanın uçtuğunu bilmez ve ona bakarak karar veremez. `finally` içinde
istisnayı yutmanın tek yolu oradan yeni bir deyimle çıkmaktır ve bu, blokta ne olduğuna
bakmadan **her** istisnayı yutar. `__exit__` ise türe bakıp seçebilir.

Seçim buradan çıkar: temizlik tek bir yerde bir kez gerekiyorsa `finally` yeter. Aynı temizlik
birden çok yerde tekrarlanıyorsa ya da karar istisnanın **türüne** bağlıysa protokol yazılır.

## Bağlanan Nesne ve Temizliğin Kendi Hatası

İki ayrıntı kaldı ve ikisi de protokolün kenarında duruyor. Birincisi `as` ile bağlanan
addır: bu ad **yöneticinin kendisi değil**, `__enter__`'ın döndürdüğü şeydir. İkincisi
`__exit__`'in kendi kırılmasıdır — temizlik de başarısız olabilir.

- **HD40** — `Kapak` yalnız `__enter__`'ın dönüşünü değiştirir; kaydı üst sınıftan alır.
  `Kirilan` yalnız temizlik sırasında bir istisna ekler.
- **HD41** — Bağlanan nesnenin kimliği `is` ile sınanır; **kimlik sayısı basılmaz**.
- **HD42** — Zincir uzunluğu birinci dersteki ölçümle **aynı yöntemle** sayılır: bastırılmamış
  bağlam ve neden bağları izlenir.

```python
"""__enter__'in donusu ve __exit__ icinde olusan istisna."""

KAYIT = []


class Izci:
    def __init__(self, ogeler=(1, 2, 3)):
        self.ogeler = list(ogeler)

    def __enter__(self):
        KAYIT.append("__enter__")
        return self

    def __exit__(self, tur, deger, iz):
        KAYIT.append("__exit__")
        return False


class Kapak(Izci):
    """__enter__ kendini degil, blogun kullanacagi baska bir nesne dondurur."""

    def __enter__(self):
        super().__enter__()
        return self.ogeler


class Kirilan(Izci):
    """Temizlik sirasinda kendisi de bir istisna uretir."""

    def __exit__(self, tur, deger, iz):
        super().__exit__(tur, deger, iz)
        raise RuntimeError("kaynak kapatılamadı")


n = Izci()
with n as bagli:
    pass
print("Izci: as ile bağlanan nesne yöneticinin kendisi mi:", bagli is n)

k = Kapak()
with k as bagli:
    pass
print("Kapak: as ile bağlanan nesne yöneticinin kendisi mi:", bagli is k)
print("Kapak: as ile bağlanan nesne __enter__'in döndürdüğü mü:",
      bagli is k.ogeler)

print()
print(f"{'durum':<36s}{'dışarı çıkan':<15s}{'bağlam':<14s}zincir")
for ad, sinif, firlat in (("blok temiz, __exit__ temiz", Izci, False),
                          ("blok istisnalı, __exit__ temiz", Izci, True),
                          ("blok temiz, __exit__ kırılan", Kirilan, False),
                          ("blok istisnalı, __exit__ kırılan", Kirilan, True)):
    try:
        with sinif():
            if firlat:
                raise ValueError("ölçüm kapsam dışı")
    except BaseException as d:
        cikan = type(d).__name__
        baglam = type(d.__context__).__name__ if d.__context__ else "-"
        uzunluk, halka = 0, d
        while halka is not None:
            uzunluk += 1
            halka = halka.__cause__ or (None if halka.__suppress_context__
                                        else halka.__context__)
    else:
        cikan = baglam = "-"
        uzunluk = 0
    print(f"  {ad:<34s}{cikan:<15s}{baglam:<14s}{uzunluk}")
```

```
Izci: as ile bağlanan nesne yöneticinin kendisi mi: True
Kapak: as ile bağlanan nesne yöneticinin kendisi mi: False
Kapak: as ile bağlanan nesne __enter__'in döndürdüğü mü: True

durum                               dışarı çıkan   bağlam        zincir
  blok temiz, __exit__ temiz        -              -             0
  blok istisnalı, __exit__ temiz    ValueError     -             1
  blok temiz, __exit__ kırılan      RuntimeError   -             1
  blok istisnalı, __exit__ kırılan  RuntimeError   ValueError    2
```

İlk üç satır `as` bağlamasını çözüyor. `Izci` kendini döndürdüğü için bağlanan ad
yöneticinin kendisi. `Kapak` başka bir nesne döndürüyor ve bağlanan ad artık yönetici
**değil**. Bir dosyayı `with` ile açtığınızda `as` ile bağlanan şeyin neden dosya nesnesi
olduğunu bu açıklar: `__enter__` onu döndürür.

Alt tablo temizliğin kendi hatasını sayıyor ve birinci dersin zincir ölçümüyle birleşiyor.
Blok da `__exit__` de temizse dışarı hiçbir şey çıkmıyor, zincir **0**. Yalnız blok
kırıldığında `ValueError` çıkıyor, zincir **1**. Yalnız `__exit__` kırıldığında `RuntimeError`
çıkıyor, zincir yine **1**.

Son satır tehlikeli olanıdır. Blok `ValueError` fırlatmış, `__exit__` çağrılmış ve o da
kırılmış. Dışarı çıkan **`RuntimeError`** — yani **temizliğin hatası**, alanın hatasının
yerine geçiyor. Asıl hata kaybolmuyor: `__context__` alanında duruyor ve zincir **2**. Ama
dışarıdaki `except ValueError` klozu artık tutmaz, çünkü uçan sınıf değişti. Bu yüzden
`__exit__` içindeki iş **kırılmamalıdır**; kırılabilecekse kendi içinde ele alınmalıdır.

## Özet

- `with` bir kısaltmadır: girerken `__enter__`, çıkarken `__exit__` çağırır ve protokolü
  tanımlamayan nesne `with` deyimine giremez.
- İstisnasız blok **2** olay üretir, istisnalı blok **3**; fazladan olan bir yöntem çağrısı
  değil, biçimin istisnayla bitmesidir — `__exit__` her ikisinde de **bir kez** çağrılır.
- `__exit__` üç argüman alır ve istisnalı blokta türü, nesneyi ve izi görür; dönüş değeri
  doğru sayıldığında istisna yutulur ve olay sayısı **3**'ten **2**'ye iner.
- İç içe iki yöneticide çağrı sayısı dört birleşimde de **4**'tür; içteki istisnayı hep görür,
  dıştaki ancak içteki yutmadığında görür.
- `__exit__` ve `finally` dört çıkış biçiminin hepsinde çalışır; ayrıldıkları yer, `__exit__`
  istisnanın **türünü görüp** seçebilirken `finally`'nin görememesidir.
- `as` ile bağlanan ad yöneticinin kendisi değil `__enter__`'ın dönüşüdür; `__exit__` kendi
  kırılırsa dışarı **temizliğin hatası** çıkar, asıl hata bağlamda kalır ve zincir **2** olur.

## Sonraki Adım

Bu kursun ölçtüğü son sözdizim biçimi geride kaldı. Şimdiye kadar her ölçüm, dilin kendi
protokollerini çağıran bir deyim ya da ifadeydi: `for`, `if`, `+`, `with`. Sonraki dersin
konusu ise dilin sözdizimi değil, **standart kitaplığın** sunduğu ayrı bir küçük dildir:
düzenli ifadeler. Orada örüntü bir dizgide yazılır, ayrı bir kural kümesine göre yorumlanır ve
eşleşme bir nesne olarak geri döner. Ölçülecek şey o nesnenin ne taşıdığı ve örüntüdeki
parantezlerin hangi sırayla numaralandığıdır.
