Python'da with deyimi ve Context Management işlemi
Python 2.5 ile birlikte gelen -with- deyimine try-except-finally deyiminin daha estetik bir hali diyebiliriz.
Bir örnek vererek anlatmak istiyorum. En klasik olanlarından bir tane use-case uyduralım;
Bir database connection'ı açılacak
Database'e bir şeyler yazılacak.
Açılan connection kapatılacak. Burada önemli olan, 2. işlem hata verdiğinde dahi bu işleme düşmesidir. Programın connection kapanmadan sonlanmaması lazım.
Örnekteki 3. maddeyi with deyimi ile gerçekleştirebiliriz.
Bu deyimle bir objeyi kullandığınızda ilk olarak object.__enter__ metodu, işiniz bittiğinde ya da hata ile sonlandığında ise object.__exit__ metodu çağırılır.
Use-case'i birazcık koda dökelim;
class DatabaseIteraction(object): def __enter__(self): print "connecting to database." return self def some_operation(self): print "updating rows..." def __exit__(self, exc_type, exc_value, traceback): print "disconnecting from database." with DatabaseIteraction() as db: db.some_operation() assert False db.some_operation()
Kodu çalıştırdığımızda şöyle bir sonuç alıyoruz;
>> connecting to database. >> updating rows... >> disconnecting from database. Traceback (most recent call last): File "/tmp/blabla.py", line 17, in raise Exception('bla bla') Exception: bla bla
Gördüğünüz gibi database'e bağlandık, bir kaç kayıt güncellendik. Kayıt güncellendikten sonra assert False diyerek bir Exception fırlatılmasını sağladık. Eğer with deyimi ile kullanmasaydık program burada sonlanacak ve database ile bağlantımızı kesmemiş olacaktık. Ancak gördüğünüz gibi burada Exception fırlatılmasına rağmen database bağlantısını kapattırabildik.
Sadece bağlantı ya da dosya açmak gibi işlemlerden ziyade with statement'ını bir çok yerde kullanabiliriz. Bir örnek daha verecek olursak, diyelim ki bir swap işlemi yapmak durumundasınız. Görüntü bakımından oldukça çirkin bir iş :)
Bunu with ile daha efektif bir şekle dönüştürebilirsiniz. Son günlerde benim hayatımı kurtaran Django 1.4 ile gelen translation paketindeki override özelliğinin kodunu direk buraya yazıyorum;
# https://github.com/django/django/blob/master/django/utils/translation/__init__.py#L94 class override(object): def __init__(self, language, deactivate=False): self.language = language self.deactivate = deactivate self.old_language = get_language() def __enter__(self): if self.language is not None: activate(self.language) else: deactivate_all() def __exit__(self, exc_type, exc_value, traceback): if self.deactivate: deactivate() else: activate(self.old_language)
Kullanımına gelirsek;
from django.utils import translation print translation.get_language() # en-us with translation.override("tr"): print translation.get_language() # tr print translation.get_language() # en-us
Eğer ufak bir işlem için with statement'ını kullanacaksanız ya da sınıf yazmak istemiyorsanız python'ın built-in kütüphanesinde contextlib diye bir paket bulunmakta. Bu paketteki contextmanager decorator'unu kullanarak bir generator fonksiyon yazabilirsiniz.
Django'daki translation override özelliğini simüle etmeye çalıştım;
active_language = "en-us" def get_language(): global active_language return active_language def activate(lang): global active_language active_language = lang @contextmanager def override(language): old_language = get_language() activate(language) yield activate(old_language) print get_language() # en-us with override("tr"): print get_language() # tr print get_language() # en-us
Önerebileceğim kaynaklar;
http://www.python.org/dev/peps/pep-0343/
http://docs.python.org/library/contextlib.html
http://docs.python.org/reference/datamodel.html#context-managers
http://www.itmaybeahack.com/book/python-2.6/html/p03/p03c07_contexts.html
http://preshing.com/20110920/the-python-with-statement-by-example












