Python Context Manager
Bu yazıda hangi konulara değineceğiz?
- Context manager nedir?
- With deyiminin kullanımı nasıldır?
- Neden böyle bir yapıya ihtiyacımız var?
- Decorator kullanımı kısa hatırlatma
- Context manager oluşturmanın yolları nelerdir?
Python programlama dilinde kaynak kullanımı ve kaynakların geri iadesi için with deyimi bulunmaktadır. With deyimi bu işlemleri context management protocol’üne uygun olarak yapmaktadır. Yani bizde bu protokole uyarak kendi sınıflarımızı, kodumuzu yazabiliriz.
Python dilini kullanan herkes bir kere de olsa dosya işlemleri yapmış yada en azından okuduğu kodlarda bu yapıyı görmüştür. Genel yapı şöyledir.
1# Dosya Aç
2# Dosyaya Yaz yada Oku (Yada herhangi bir işlem yap)
3# Dosyayı kapat
Context Manager Nedir?
-
Context manager kullanılan kaynakların geri iade edilmesi için ortaya çıkmış bir protokoldür. Bu protokolü kullanabilmemiz için belli kurallara uygun sınıflar, yapılar oluşturmamız gerekir.
-
Context manager protokolüne uygun yazdığımız sınıflarımızı kullanırken de with deyiminden yararlanacağız.
With Deyiminin Kullanımı
- with deyiminin kullanımını ve bize getirdiği kolaylığı görmek için yukarıda bahsettiğimiz dosya işlemlerini hem with deyimini kullanarak hem de with deyimi olmadan nasıl yazabileceğimize birer örnek verelim.
İlk olarak with deyimini nasıl kullanıyoruz?
1with <ifade> as degiskenIsmi:
2 <islemler>
1with open("dosyaismi.txt", "w") as fw:
2 fw.write("with deyimi kullanimi...")
with deyimi olmasaydı bu işlemi güvenli bir şekilde nasıl yapabilirdik?
1try:
2 # Dosya ac
3 file = open('kerem.txt', 'w')
4 # Islem yap
5 file.write("Lisp dili candır... o_O")
6except IOError:
7 # Exception IO HATA MESAJI
8finally:
9 # Dosya kapa
10 file.close()
-
Kısaca açıklamak gerekirse temel yapılan hatalardan bir tanesi dosyayı açıp, işlemleri yaptıktan sonra dosyayı kapamayı unutmak büyük bir problem. Çünkü kullandığımız kaynaklar sınırsız değiller. Devamlı kaynak edinip geri iade etmessek işletim sisteminiz size engel olacaktır.
-
Ama bizim yazdığımız kodda finally bloğu her zaman çalışacaktır ve dosya kapama işlemini yapacaktır.
-
with kullanım alanı sadece dosya işlemleri değil elbette. Database bağlantıları, thread kullanım alanlarda lock mekanizmasında çokça kullanılıyor. Bu kısım oluşturmuş olduğunuz dizayna bağlıdır, yani genişletilebilir.
Kaynaklar sınırsız derken ne demek istiyorsunuz?
- Hemen küçük bir kod ile açıklamaya çalışalım. Kodumuz şu şekilde çalışsın. 100_000 dosya açmaya çalışalım ve bu açtığımız dosyaları kapatmayalım. Bakalım neler olacak?
1files = list()
2
3for x in range(100_000):
4 files.append(open(f'files/{str(x)}', 'w'))
Kullandığınız işletim sistemine göre kaynak kullanımı değişiklik gösterecektir. Ben şu şekilde bir hata alıyorum. Hata da gördüğünüz gibi OSError
1OSError: \[Errno 24\] Too many open files: 'files/4861'
Neden böyle bir yapıya ihtiyacımız var?
- Bu hataları engellemek için kullandığımız kaynakları iade etmeliyiz. Bunun içinde pythonda context manager protokolü ve with deyimi var.
Context manager oluşturmanın yolları nelerdir?
- with deyimi ve context manager protokolünün ne olduğunu, neden kullanmamız gerektiğini anladığımıza göre artık nasıl bu protokole uygun sınıflar yazabiliriz, onun üzerinde duralım.
Context manager oluşturmanın 2 yolu var.
- Class Kullanmak
- contexmanager decorator kullanmak
Context manager protokolüne uygun class oluşturma
Bir sınıfın context manager protokolüne uygun olabilmesi için __enter__ ve __exit__ metotlarını bulundurması gerekiyor.
- __enter__ metodu self dışında herhangi bir parametre almaz
- __exit__ metodu self dışında 3 parametre daha alıyor. (type, value, traceback)
Basit bir örnek yazalım ve nasıl çalıştığını anlayalım.
1#!/usr/bin/env python3
2
3class Kerem:
4
5 def __init__(self):
6 print("init metodu calisti...")
7
8 def __enter__(self):
9 print('__enter__ metodu cagirildi...')
10 return self
11
12 def __exit__(self, exc_type, exc_value, traceback):
13 print('__exit__ metodu cagirildi...')
14
15with Kerem() as f:
16 print("with blogunun icindeyim...")
1init metodu calisti...
2__enter__ metodu cagirildi...
3with blogunun icindeyim...
4__exit__ metodu cagirildi...
Çalışma akışını gördünüz. Sınıfımızı context manager protokolüne uygun şekilde yazdık ve daha sonra with deyimi ile birlikte kullandık.
- Önce __init__ çalıştı.
- İkinci olarak __enter__ metodu içinde ki işlem yapıldı ve işlem sonucu return ile geri dönüldü.
- with deyimi içerisindeki alan işlendi. Burada bir değişkene return edilen değeri atadık vb işlemler.
- with bloğu bittiği gibi __exit__ metodu çağırıldı.
Artık çalışma yapısını da biliyoruz. Yukarıda ilk verdiğimiz dosya işlemleri üzerine bir örnek yazalım.
1#!/usr/bin/env python3
2
3class File(object):
4
5 # Constructor metodumuzu yazalim
6 def __init__(self, file_name, mode):
7 self.file_name = file_name
8 self.mode = mode
9
10 def __enter__(self):
11 # Islemleri yapildigi metod
12 self.f_obj = open(self.file_name, self.mode)
13 return self.f_obj
14
15 def __exit__(self, exc_type, exc_val, traceback):
16 # Dosya kapama islemi
17 self.f_obj.close()
18
19
20with File('kerem.txt', 'w') as f_write:
21 f_write.write('Test...')
22 print(f"Dosya Kapatildi mi?[With Body] -> {f_write.closed}")
23
24print(f"Dosya Kapatildi mi?[With blogu disi] -> {f_write.closed}")
1Dosya Kapatildi mi?\[With Body\] -> False
2Dosya Kapatildi mi?\[With blogu disi\] -> True
Bu kadar örnek yeterli diye düşünüyorum. Şimdi ikinci bir yolumuz olan decorator yapısını kullanarak nasıl contextmanager protokolünü destekleyen kod yazabiliriz ona geçelim.
ContexManager Decorator Kullanımı
- Diğer yöntem ise python dilinin sunduğu bir kütüphaneyi kullanmak. Bu kütüphanenin adı contextlib. Bu kütüphane içinde ki contextmanager decorator’ünü kullanacağız.
Kısaca decoratör yapısını hatırlayalım…
- Decorator yapılar parametre olarak fonksiyon alıp geriye fonksiyon döndüren yapılardır. İki tür decorator yapısı var.
- class decorators
- function decorators
1# Kullanımı
2
3@<fonkismi>(<varsa\_parametreler>)
4def func():
5 pass
Ufak bir hatırlatmadan sonra örneğimize geri dönelim.
1#!/usr/bin/env python3
2
3from contextlib import contextmanager
4
5@contextmanager
6def dosyaisleme(dosya_adi, mod):
7 dosya = open(dosya_adi, mod) # __init__ islemi
8 yield dosya # __enter__ islemi
9 dosya.close() # __exit__ islemi
10
11with dosyaisleme('kerem.txt', 'w') as f_write:
12 f_write.write('Test...')
13 print(f"Dosya Kapatildi mi?[With Body] -> {f_write.closed}")
14
15print(f"Dosya Kapatildi mi?[With blogu disi] -> {f_write.closed}")
1Dosya Kapatildi mi? [With Body] -> False
2Dosya Kapatildi mi? [With blogu disi] -> True
- contextmanager decorator yapısının kodunu incelersek contextlib.py içerisinde ki _GeneratorContextManager isimli sınıfı kullandığını göreceğiz. Sınıf kodunu incelersek tıpkı bizim yazdığımız yapı gibi __init__ __enter__ ve __exit__ metotlarını göreceksiniz.
Yararlanabileceğiniz diğer kaynaklar:
Baris Simsek https://www.youtube.com/watch?v=XcY4jDj4VkE
PythonTips http://book.pythontips.com/en/latest/context_managers.html
Jeff Knupp https://jeffknupp.com/blog/2016/03/07/python-with-context-managers/