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),withbloğ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:
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:
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:
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:
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@decoratorolarak 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 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?
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?
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.