fatiherikli

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;

  1. Bir database connection'ı açılacak
  2. Database'e bir şeyler yazılacak.
  3. 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](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;

comments powered by Disqus