Closures konusuna direkt giriş yapmadan önce iç içe fonksiyonları ve local olmayan değişkenleri bir hatırlayalım. Bir fonksiyonun diğer bir fonksiyonun içinde olmasına iç içe fonksiyonlar (nested functions) olarak adlandırıyoruz. İç içe fonksiyonlarda, iç fonksiyon base fonksiyon içindeki değişken/lere erişebilir. Direkt bir örnekle başlayalım.
1 2 3 4 5 6 7 8 9 10 11 |
def yaz_msg(msg): # Dış bir değişken, def yaz(): # İçteki fonksiyondan bu değişkene ulaşılıyor. print(msg) yaz() # Test edelim yaz_msg("Merhaba") |
1 |
Merhaba |
Fonksiyonlar konusu hatırlarsak, fonksiyon dışındaki bir değişken local bir değişken olarak kabul ediliyordu. İç içe fonksiyonlarda ise durum tamamen farklı, bu değişkenler yukarıdaki gibi kullanılabiliyor.
14.1.1. Bir Closure Fonksiyonu Tanımlama
Bir üsteki örnekte yaz() iç fonksiyonunu direkt yaz_msg fonksiyonu içinde çağırdık. Eğer bu fonksiyonu fonksiyon dışından çağırmak istersek closure konusu karşımıza çıkar. Örneğin,
1 2 3 4 5 6 7 8 9 10 |
def yaz_msg(msg): def yaz(): print(msg) return yaz # Test edelim diger = yaz_msg("Merhaba") diger() |
Fonksiyon içinde iç fonksiyonu çağırmak yerine direkt return ediyoruz. Aşağıda fonksiyondan bir tane oluşturup direkt fonksiyon gibi çağrıyoruz, bu durumda yaz fonksiyonu dolaylı yoldan metni yazar. İşte bu bağlama işlemine Closure denir.
Acaba yaz_msg ve diğer arasında nasıl bir bağlantı oluştu. Acaba referans türünden bir bağlantı mı yoksa değer türünden bir bağlantı mı? Eğer referans türünden ise yaz_msg silindiğinde diger’de silinir. Test edelim:
1 2 3 |
del yaz_msg diger() yaz_msg("Hello") |
1 2 3 4 5 6 7 8 9 10 |
Merhaba --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-39-276dcc381573> in <module>() 1 del yaz_msg 2 diger() ----> 3 yaz_msg("Hello") NameError: name 'yaz_msg' is not defined |
Görüldüğü gibi yaz_msg silinmesine rağmen diger bağlantısı çalışıyor. Ama beklenildiği gibi yaz_msg artık çalışmıyor.
14.1.2. Closure kullanımı
Yukarıdaki örnekte görüldüğü gibi, iç içe bir fonksiyon, kendi kapsama alanında bir değere başvurabildi. Bir closure yaratabilmek için:
- İç içe bir fonksiyon olmalı
- Bir üst fonksiyondaki değer kullanılabilir
- Kapsanan fonksiyon, iç içe fonksiyonda (base fonksiyon) döndürülmelidir.
14.1.3. Ne zaman Closure kullanılır?
Klasik bir fonksiyonda dışarıdaki değerleri kullanmak için ya global yapmalı ya da referans türünden fonksiyona gönderilmelidir. Ama daha esnek bir yapı kurmak istersek iç içe fonksiyonları kullanabiliriz. Bu noktada Closure konusu esnek ve güzel bir çözüm olarak düşünülebilir. Basit bir örnekle bu konuyu nerelere gidebileceğini gösterelim:
1 2 3 4 5 6 7 8 9 10 11 |
def carpma_yap(n): def carp(x): return x * n return carp kere3 = carpma_yap(3) kere5 = carpma_yap(5) print(kere3(9)) print(kere5(3)) print(kere5(kere3(2))) #esnekliğe bir örnek |
Yukarıda çarpma yapan bir iç içe fonksiyon tanımladık. n değerini başlangıçta x değerine de sonradan aldık. Hatta son çıktıda yarattığımız iki closure işlemini iç içe çağırdık. Çıktısı:
1 2 3 |
27 15 30 |
Decorator konusu closure konusunun biraz daha genişletilmiş halidir. O sebepten dolayı decorator konusuna geçmeden önce bu konuyu daha iyi anlamak için farklı örneklerde araştırabilirsiniz.
Aslında closure tüm fonksiyonlarda bulunan bir özelliktir. Bir cell nesnesi döndürür ve bu nesneye ulaşabilirsiniz. Örneğin,
1 2 |
carpma_yap.__closure__ kere3.__closure__ |
1 |
(<cell at 0x0000017053164108: int object at 0x00007FF88BDFEF40>,) |
Cell içine erişelim.
1 |
kere3.__closure__[0].cell_contents |
1 |
3 |