Bağlam yöneticileri, kaynakların garantili bir şekilde sona erdirilmesini sağlayarak kod bloklarının otomatik olarak ve kontrol altında çalıştırılmasını sağlar - örneğin, dosyaları otomatik olarak kapatmak, veritabanındaki işlem yöneticilerini serbest bırakmak veya akışları kilitlemek/kilidini açmak. Python'da bu paradigma, dış kaynakları manuel olarak son aşamanın takibi olmadan güvenli ve kolay yönetmek amacıyla sunulmuştur.
Tarihçe:
Bağlam yöneticileri öncesinde kaynakları serbest bırakmak için close()/release() yöntemlerinin açıkça çağrılması gerekiyordu, bu da istisnalar meydana geldiğinde hatalara yol açıyordu. with ... as ...: yapısının ortaya çıkmasıyla Python, "kaynağın yaşam alanını" açıkça tanımlayarak, ona başlama ve bitirme yöntemlerini otomatik olarak çağırma imkanı tanıdı.
Problem:
Kaynağın "kapalı" kalmasını elle yönetmek tehlikedir - close() (veya release()) çağrısının unutulması durumunda, kaynaklar süreç sona erene kadar veya kalıcı olarak bağlı kalabilir. Bu, dosyalar, ağ bağlantıları, veritabanı işlemleri ile çalışırken özellikle önemlidir.
Çözüm:
Bağlam yöneticileri, enter() ve exit() sihirli yöntemleri aracılığıyla uygulanır. with bloğuna girildiğinde, Python enter'ı çağırır ve bloğun dışına çıkıldığında exit'ı çağırır, bu, blok içinde bir istisna oluşsa bile geçerlidir.
Kod örneği:
class ManagedFile: def __init__(self, filename): self.filename = filename self.file = None def __enter__(self): self.file = open(self.filename, 'w') return self.file def __exit__(self, exc_type, exc_val, exc_tb): if self.file: self.file.close() # İstisnasını bastırmak için return True, genellikle return False with ManagedFile('demo.txt') as f: f.write('Merhaba, dünya!\n') # dosya garanti kapatılmıştır
Anahtar özellikler:
Aynı bağlam yöneticisi örneğini art arda farklı with bloklarında kullanmak mümkün mü?
Hayır: genellikle kaynak enter'da açılır, exit'te serbest bırakılır ve nesne tekrar kullanılamaz hale gelir. Her with bloğu için yeni bir nesne oluşturmak daha iyidir.
Eğer birden fazla kaynağı tek bir with içinde kullanmak gerekiyorsa ne yapılmalı?
Değişkenleri virgülle ayırabilirsiniz:
with open('a.txt') as fa, open('b.txt') as fb: ...
veya karmaşık durumlar için contextlib.ExitStack kullanılabilir.
Bağlam yöneticisini enter/exit ile bir sınıf olarak yazmak ile @contextmanager dekoratörü ile bir jeneratör olarak yazmak arasındaki fark nedir?
contextlib modülündeki @contextmanager dekoratörü, yönetici oluşturmayı yield aracılığıyla daha kolay hale getirir:
from contextlib import contextmanager @contextmanager def open_file(name): f = open(name) try: yield f finally: f.close() with open_file('file.txt') as f: ...
Geliştirici bir dosyayı open() ile açar, içine yazar ve close() çağrısını unutursa - dosya açık kalabilir (özellikle hatalar durumunda), veriler kaydedilmez.
Artılar:
Eksiler:
her yerde with open() kullanıldı (veya özel yönetilen kaynak), bağlam yöneticisi kaynağı düzgün serbest bırakma işlemini uygular.
Artılar:
Eksiler: