Menu
Playground'da Dene

Python Bağlam Yöneticileri: with Deyimi, Ayrıntılı Açıklaması

with deyimi gerçekte ne yapıyor — dosyalar, kilitler, veritabanı bağlantıları ve güvenilir bir kapanış gerektiren her şey için otomatik temizlik.

Kendisinden Sonra Temizlik Yapan Deyim

Bir programda açtığın her kaynak — bir dosya, bir ağ bağlantısı, bir veritabanı tutamacı, bir kilit — işin bittiğinde kapatılmak zorundadır. Unutursan bellek sızdırırsın, başka süreçleri bloke eden kilitler tutarsın ya da çökmede dosyaları bozarsın. Python'un with deyimi bunu senin için halleder.

Herkesin ilk tanıştığı kalıp bir dosya okumaktır:

with open("notes.txt") as f:
    contents = f.read()
    print(contents)

Otomatik olarak iki şey olur. Girişte open() sana f'e bağlı bir dosya nesnesi verir. Çıkışta — blok normal bitsin, erken return yapsın ya da istisna fırlatsın — Python senin için f.close()'u çağırır.

O kadar. with'in tüm amacı bu.

"With" Neyin Yerine Geçer

Bağlam yöneticilerinden önce, buna denk güvenli kod bir try/finally idi:

f = open("notes.txt")
try:
    contents = f.read()
    print(contents)
finally:
    f.close()

"Bir dosya oku ve iş bittiğinde kapat" için beş satır tören. Bunu büyük bir programdaki her open ile çarp, çekiciliği görmeye başlarsın. with daha kısa, yanılmak daha zor ve temizliği unutmak imkânsız.

Birden Fazla Kaynağı Açmak

Tek bir with'te birkaç bağlam yöneticisi bağlayabilirsin:

with open("input.txt") as src, open("output.txt", "w") as dst:
    dst.write(src.read().upper())

Girişte iki dosya da açılır, çıkışta iki dosya da kapanır. İlk open başarılı olup ikincisi fırlatırsa Python yine de ilkini kapatır — makine kısmi kurulumu doğru yönetir.

Daha uzun kaynak listeleri için parantezli form (Python 3.10+) daha net:

with (
    open("a.txt") as a,
    open("b.txt") as b,
    open("c.txt") as c,
):
    ...

Bağlam Yöneticisi Aslında Nedir

__enter__ ve __exit__ tanımlayan her nesne bir bağlam yöneticisidir. Protokol son derece basittir:

  • __enter__(self), with bloğu başladığında çalışır. Döndürdüğü değer, as name'in neye bağlanacağıdır.
  • __exit__(self, exc_type, exc_value, traceback), blok nasıl biterse bitsin çalışır. Çıkışa bir istisna neden olduysa, bağlam yöneticisinin onu inceleyebilmesi ya da bastırabilmesi için istisna bilgisi iletilir.

İşte sardığı bloğu zamanlayan minimal bir örnek:

main.py
Output
Click Run to see the output here.

with Timer(): nesneyi oluşturur, __enter__'unu çağırır, gövdeyi çalıştırır, __exit__'i çağırır. Dosya yok, kilit yok — sadece "bir şey yap, ne kadar sürdüğünü ölç"ün üstüne küçük bir sarmalayıcı.

contextlib.contextmanager Kısayolu

Her bağlam yöneticisi için bir sınıf tanımlamak gerekenden daha ağırdır. contextlib.contextmanager, bir generator fonksiyonunu bağlam yöneticisine çevirir — tek bir yield, "önce"yi "sonra"dan ayırır:

main.py
Output
Click Run to see the output here.

yield'den önceki her şey __enter__ davranışıdır. Sonrası __exit__'tir. try/finally, gövde fırlatsa bile temizliğin çalışmasını sağlar.

Yazacağın çoğu özel bağlam yöneticisi bu şekle oturur. Önce decorator formuna uzan; yalnızca generator formunun ifade edemediği bir şeye ihtiyacın olduğunda sınıfa in.

Geçici Olarak Bir Şeyi Değiştirmek

Yaygın bir şekil: bir şeyi ayarla, kullan, geri yükle. Bağlam yöneticileri bunu temiz ifade eder:

main.py
Output
Click Run to see the output here.

Her türlü "ayarla, sonra geri yükle" kalıbı — ortam değişkenleri, log ayrıntı seviyesi, feature flag'lar, test fixture'ları — doğal olarak bir bağlam yöneticisine oturur. Çağıranlar hiçbir şeyi geri yüklemeyi hatırlamak zorunda kalmaz.

İstisnaları Bastırmak

__exit__ metodu, Python'a "istisnayı ben hallettim; yut" demek için True döndürebilir. Bu nadirdir ve genellikle kötü bir işarettir ama contextlib.suppress böyle çalışır:

main.py
Output
Click Run to see the output here.

suppress(FileNotFoundError), FileNotFoundError'ı bir no-op'a çevirir. Gerçekten isteğe bağlı işlemler için kullan — "bunu dene, yürümezse umurumda değil." Düşünmediğin istisnaları susturmak için kullanma.

Karşılaşacağın Diğer Bağlam Yöneticileri

Bağlam yöneticileri standart kütüphanenin her yerinde karşına çıkar, bir kez bakmaya başladın mı:

import threading
from pathlib import Path

# Locks — guarantee release even if the critical section raises.
lock = threading.Lock()
with lock:
    ...

# tempfile — delete the temp file when you're done.
from tempfile import TemporaryDirectory
with TemporaryDirectory() as tmp:
    path = Path(tmp) / "scratch.txt"
    path.write_text("hello")

# Database connections — close the connection (or end the transaction).
import sqlite3
with sqlite3.connect(":memory:") as conn:
    conn.execute("CREATE TABLE t (x INTEGER)")

Üçüncü parti kütüphaneler de aynı kuralları izler. with something as x: gördüğünde bu neredeyse her zaman "x'i bu bloğun süresince kullan ve sonrasında temizle" anlamına gelir.

with Ne Zaman Kullanılmaz

  • Aslında kurulum ve temizlik olmadığında. Gelişigüzel kodu hiç sebep yokken bir bağlam yöneticisine sarmak gürültü yaratır.
  • Kaynağa birçok ilişkisiz blokta ihtiyacın olduğunda. Uzun bir script'in tüm ömrü boyunca bir with'i açık tutmak, temizlik kapsamının aslında ne olduğunu gizler. Kaynağa sahip bir sınıf düşünmek daha iyi olabilir.
  • Decorator daha iyi oturduğunda. Bazı tekrar eden kalıplar (retry, log, time) bir fonksiyonun içindeki with ...: yerine fonksiyonun üstündeki @decorator olarak daha doğal okunur. Çağrı yerinde daha iyi okunanı seç.

Çoğu zaman with doğrudur. Nadir istisnaları, aradığında görmek kolaydır.

Sırada: Gerçek Dosyalarla Çalışmak

Artık zamanın yüzde doksanında kullanacağın with open(...) as f:'in arkasındaki mekanizmayı biliyorsun. Bir sonraki bölüm, bunu diskte dosya okumak, yazmak ve gezmek için iş başına koyuyor.

Sıkça Sorulan Sorular

Python'da with open ne yapar?

with open(path) as f: dosyayı açar ve blok süresince f'e bağlar. Blok biter bitmez — normal bitsin ya da bir istisna yüzünden — Python dosyayı otomatik kapatır. f.close()'a ihtiyacın yok; with deyimi bunu garanti eder.

Neden düz open() yerine with kullanayım?

Çünkü with, blok ortasında bir istisna çıksa bile dosyayı kapatır. Düz open() seni her kod yolunda — hata yolları dahil — close()'u hatırlamakla yükümlü bırakır. with hem daha güvenli hem daha kısadır.

Tek bir with ile birden fazla dosyayı nasıl açarım?

Bağlam yöneticilerini virgülle ayır: with open('a.txt') as a, open('b.txt') as b:. Girişte her iki dosya açılır, çıkışta ters sırayla kapatılır. Aynı anda birkaç kaynağa ihtiyacın olduğunda iç içe with deyimlerinin yerine geçer.

Coddy ile kodlamayı öğren

BAŞLA