İçindekiler
Bir dosyayı açtınız, veritabanına bağlandınız veya bir kilidi ele geçirdiniz. Tam işinizi bitirecekken kod bir istisna fırlattı! Kaynağı serbest bırakmayı unuttuysanız dosya tanıtıcıları tükenebilir, bağlantılar açık kalabilir ya da uygulamanız kilitlenebilir. Python’ın with ifadesi, işler yolunda gitse de gitmese de temizlik adımının çalışmasını sağlayarak bu problemi zarifçe çözer.
``
Temel fikir: edin, kullan, mutlaka bırak
Kaynak yönetimini üç aşamalı bir süreç olarak düşünebiliriz:
- Kaynağı edin.
- Kaynağı kullan.
- Kaynağı serbest bırak.
Bir kaynağın edinilme olasılığı $P(E)$ ve kullanım sırasında istisna oluşma olasılığı $P(H)$ olsun. Elle yazılmış dikkatsiz bir kodda temizliğin atlanma riski yaklaşık olarak $P(E) \times P(H)$ ile ilişkilendirilebilir. Context manager ise kontrol akışını merkezileştirerek istisna yolunda da temizliği devreye sokar.
with open("veriler.txt", "r", encoding="utf-8") as dosya:
icerik = dosya.read()
sayi = int(icerik) # ValueError oluşabilir
int() başarısız olsa bile dosya kapatılır. Buradaki garanti, with ifadesinin sihrinden değil, context manager protokolünden gelir.
Perde arkasında ne oluyor?
Bir context manager iki özel metot uygular:
__enter__(): Blok başlamadan önce çalışır veasdeğişkenine verilecek değeri döndürür.__exit__(): Blok normal veya hatalı biçimde tamamlandığında çalışır.
Yukarıdaki kod, kavramsal olarak şu yapıya benzer:
manager = open("veriler.txt", "r", encoding="utf-8")
dosya = manager.__enter__()
try:
icerik = dosya.read()
sayi = int(icerik)
except BaseException as hata:
bastirildi = manager.__exit__(
type(hata), hata, hata.__traceback__
)
if not bastirildi:
raise
else:
manager.__exit__(None, None, None)
Gerçek Python uygulamasının ayrıntıları farklı olabilir; ancak mantık budur. İstisna oluşursa türü, nesnesi ve traceback bilgisi __exit__() metoduna aktarılır. Metot True döndürürse istisna bastırılır; False veya None döndürürse dışarı yayılmaya devam eder.
| Durum | __exit__() argümanları |
Sonuç |
|---|---|---|
| Blok normal biter | None, None, None |
Temizlik yapılır |
| İstisna oluşur | Tür, nesne, traceback | Temizlik yapılır, hata genellikle yayılır |
__exit__() True döndürür |
İstisna bilgileri | Hata bastırılır |
__exit__() hata fırlatır |
Değişebilir | Yeni hata dışarı yayılır |
Kendi context manager’ımızı yazalım
Aşağıdaki sınıf, hayali bir bağlantının yaşam döngüsünü yönetir:
class Baglanti:
def __enter__(self):
print("Bağlantı açıldı")
self.aktif = True
return self
def sorgula(self):
if not self.aktif:
raise RuntimeError("Bağlantı kapalı")
raise ValueError("Sorgu başarısız oldu")
def __exit__(self, hata_turu, hata, traceback):
self.aktif = False
print("Bağlantı kapatıldı")
return False
with Baglanti() as baglanti:
baglanti.sorgula()
ValueError oluşmasına rağmen __exit__() çağrılır ve bağlantı kapatılır. False döndürüldüğü için hata gizlenmez. Bu genellikle en güvenli davranıştır; çünkü hataları sessizce yutmak, böcekleri görünmez yapan dijital bir halı altı süpürgesidir.
try/finally ile farkı nedir?
| Yaklaşım | Avantaj | Dezavantaj |
|---|---|---|
try/finally |
Açık ve temel kontrol sağlar | Her kullanımda tekrar gerekir |
with |
Kısa, okunabilir ve yeniden kullanılabilir | Context manager tanımlanmalıdır |
try/except |
Hataları işlemek için uygundur | Tek başına temizliği garanti etmez |
Aslında with, try/finally düşüncesinin yeniden kullanılabilir bir nesne protokolüne dönüştürülmüş hâlidir. contextlib.contextmanager dekoratörüyle aynı yapı bir generator kullanılarak da kurulabilir; yield öncesi edinme, yield sonrası ise temizlik bölümüdür.
Sonuç olarak with, yalnızca dosya açmanın kısa yolu değildir. Dosyalar, ağ bağlantıları, kilitler, geçici dizinler ve transaction’lar gibi kaynaklarda yaşam döngüsünü tek noktadan yönetir. En önemli güvence şudur: Bloktan normal çıkışta, return sırasında veya bir istisna nedeniyle ayrılırken __exit__() çalıştırılır. Böylece kaynak temizliği, geliştiricinin hafızasına değil dilin kontrol akışına emanet edilir.
Yorumlar