Tarihçe — Python'da kaynakları (dosyalar, bağlantılar, işlemler) yönetmek için enter, exit protokolüne dayalı with yapısı kullanılır. Basit durumlar için bir sınıf yazmak fazla, bu nedenle contextlib modülünden @contextmanager dekoratörü önerilmiştir; bu, kaynak yöneticilerini jeneratörler olarak tanımlamaya olanak tanır.
Sorun — kaynakları manuel olarak serbest bırakmak veya kapatmak rahatsız edicidir, kod hatalara karşı dayanıklı olmaz (örneğin, dosyayı kapatmayı unuttuk). Ayrıca basit şeyler için (örneğin, geçici olarak dizin veya stdout değiştirmek) iki metodu olan ayrı bir sınıf yazmak istemezsiniz.
Çözüm — @contextmanager kullanarak kaynağın "başlangıcını" ve "sonunu" ele almak, istisnaları ve serbest bırakmayı garanti eder.
Kod örneği:
from contextlib import contextmanager @contextmanager def open_file(filename, mode): f = open(filename, mode) try: yield f finally: f.close() with open_file('test.txt', 'w') as f: f.write('Hello')
Anahtar özellikler:
@contextmanager'dan yield ile birden fazla nesne döndürmek mümkün mü (örneğin, bir demet üzerinden)?
Evet, mümkündür ve ilişkili "grup" kaynaklarını döndürmek için kullanışlıdır.
Kod örneği:
@contextmanager def managed_two(): a, b = [], {} try: yield a, b finally: a.clear(); b.clear()
Yield'den sonra bir istisna atılırsa — kaynak kapanır mı?
Evet, finally bloğu her durumda çalıştırılır, even if bir hata/istisna oluşursa with içinde.
@contextmanager tam bir sınıf bağlam yöneticisini enter/exit ile değiştirebilir mi?
Çoğu basit durumda — evet, daha karmaşık olanlarda iç içe durumlar veya miras ile çalışmak daha kolaydır sınıf üzerinden.
Dosyayı manuel olarak açıp kapatıyorlar:
f = open('test.txt', 'w') try: f.write('Hello') finally: f.close()
Artıları:
Eksileri:
Çalışma dizinini geçici olarak değiştirmek ya da dosya açmak (veya ortam ayarları): @contextmanager kullanıyorlar:
@contextmanager def work_in(dirname): import os prev = os.getcwd() os.chdir(dirname) try: yield finally: os.chdir(prev)
Artıları:
Eksileri: