First-Class Function, Closure ve Decorator

Bu yazıyı hazırlarken Corey Schafer‘ın video derslerinden yararlandım.

Bir fonksiyonu değişkene atarsak ne olur? First class function (kaba bir çeviri olacak ama) -birinci sınıf fonksiyonlar- terimi bu işe yarıyor. Yani fonksiyonları değişkenlere atayıp değişkeni fonksiyon olarak kullanabiliyoruz. Şu örneği inceleyelim:

def square(num):
    print (num * num)

x = square
x(5)

Burada kendisine argüman olarak verilen sayının karesini ekrana basan bir fonksiyonum var. Bu fonksiyonu x değişkenine atıyorum. Ancak atama yaparken dikkat ettiyseniz parantez kullanmadım. Çünkü parantez kullanırsam atama işlemi yerine fonksiyonu koşmaya çalışır. Ben burada fonksiyonu çalıştırmadan bir nevi fonksiyonu değişkene kopyaladım.

Artık x değişkenini fonksiyon çağırır gibi kullanabilirim. Kodu çalıştırdığınızda ekrana 25 yazdırdığını görebilirsiniz.

Temelde closure ve decorator kavramları da buradan çıkıyor. Bunların hepsi birbiriyle ilişkili yapılar. Closure (çev. kapatma!) iç içe fonksiyonlarda içteki fonksiyonun dıştaki fonksiyon değişkenlerine erişimini sağlayan bir özellik. Burada da birinci sınıf fonksiyonları kullanacağız. İşte örnek:

def outer_func():
    message = "Penguen"
    
    def inner_func():
        print(message)

    return inner_func()

outer_func()

Kodu çalıştırdığımda neler oluyor bakalım. outer_func() ile dıştaki fonksiyonu çağırıyorum. Bu fonksiyonun içinde message adında bir değişken ve inner_func fonksiyonu yer alıyor. Geriye ise inner_func fonksiyonunu döndürüyor. inner_func fonksiyonunun içeriğine bakarsak message değişkenini ekrana yazdırdığını görürüz. Ancak aslında message değişkeni dıştaki fonksiyona ait.

Gerçekten de kodu çalıştırdığımda ekrana Penguen yazdığını görüyorum. Burada eğer return inner_func() satırını kullanmasaydım programım çalışmazdı. Closure yapısı bu açıdan bize büyük avantajlar sağlayabiliyor. Bir de şuna bakın:

def outer_func(msg):
    message = msg

    def inner_func():
        print(message)

    return inner_func

my_msg = outer_func("Penguen")
my_msg()

Bu örnekte de first-class yapısını kullandım. Sonuç aynı.

Decorator (Türkçe karşılığını bulamadım), argüman olarak fonksiyon alan ve aldığı fonksiyonu çalıştırıp geriye fonksiyon döndüren yapıdır. Biraz karışık oldu. Örnek kodla göstermeliyim.

def decorator_func(original_func):
    def wrapper_func():
        return original_func()
    return wrapper_func

def display():
    print("display function run")

my_display = decorator_func(display)
my_display()

display fonksiyonunu decorator_func fonksiyonuna argüman olarak verdim. wrapper_func fonksiyonu içinde display fonksiyonumu çalıştırdım ve geriye döndürdüm. Son olarak da wrapper_func fonksiyonumu geri döndürerek my_display değişkenine atadım. my_display değişkenini fonksiyon gibi çağırarak ekrana display function run yazısı yazdırdım. Bu kullanımı yazının başında göstermiştim.

Ancak bu kullanım şekli çok tercih edilmez. Bunun yerine decorator fonksiyonlar için “@” işareti kullanılır. Yani

def decorator_func(original_func):
    def wrapper_func():
        print("wrapper executed")
        return original_func()
    return wrapper_func

@decorator_func
def display():
    print("display function run")

my_display = decorator_func(display)
my_display()

Burada ek olarak wrapper_func fonksiyonuna bir işlev daha ekledim. Bunu @decorator_func tanımlamasınının farkını anlamak için test edeceğiz. Bu tanımlamayı yaptıktan sonra artık display fonksiyonunu kendi başına çağırabilirim. Yani şöyle bir kullanım da aynı sonucu verecektir:

def decorator_func(original_func):
    def wrapper_func():
        print("wrapper executed")
        return original_func()
    return wrapper_func

@decorator_func
def display():
    print("display function run")

display()

Burada çıktı

wrapper executed
display function run

şeklinde olur. Görüldüğü üzere @decorator_func tanımlamasını çıkarırsam herhangi bir ilişkilendirme olmaycağından ekrana sadece display function run yazılacaktır. Yani display fonksiyonu sıradan bir şekilde tek başına koşulur.

Decorator yapısı fonksiyonlar yerine sınıflar için de kullanılabilir.

class decorator_class(object):
    def __init__(self, original_func):
        self.original_func = original_func

    def __call__(self, *args, **kwargs):
        print("__call__ method executed")
        return self.original_func()

@decorator_class
def display():
    print("display function run")

display()

Farklı olarak burada __call__ metodunu kullandım. Bu metod önceki örnekte yer alan wrapper_func fonksiyonu gibi düşünülebilir. İsimler ve ufak değişikliklerle beraber bildiğimiz sınıf yapısıyla aynı.

Akılda kalan son soru işaretini cevaplayıp örnek bir kod vererek yazımı sonlandıracağım. Biz bunu nerede kullanacağız? Genelde loglama işlemleri, stream gerektiren uygulamalar, fonksiyon zamanlaması, erişim kontrolü gibi konularda decorator yapısı kullanılır. İşte örnek kod:

def my_logger(orig_func):
    import logging
    logging.basicConfig(filename = "{}.log".format(orig_func.__name__), level = logging.INFO)

    def wrapper(*args, **kwargs):
        logging.info("Ran with args: {}, and kwargs: {}".format(args, kwargs))
        return orig_func(*args, **kwargs)

    return wrapper

@my_logger
def display_info(name, age):
    print("display_info ran with arguments ({},{})".format(name, age))

display_info("John", 25)

Basit bir loglama işlemi yapan bu kodu çalıştırdığımda display_info.log adında bir dosya oluşturacak. Ve içine parametre olarak verdiğim argümanları kaydedecek. display_info() fonksiyonuna verdiğim argümanları değiştirip tekrar çalıştırdığımda yeni argümanları log dosyasına ekleyecektir. Log dosyası çıktısı şu şekilde olur:

INFO:root:Ran with args: ('John', 25), and kwargs: {}
INFO:root:Ran with args: ('Hank', 30), and kwargs: {}

Eksik veya hatalı bir bilgi varsa üstteki sosyal paylaşım linklerinden bana ulaşıp bildirim yapabilirsiniz.

Bu yazı Python içinde yayınlandı ve , , olarak etiketlendi. Kalıcı bağlantıyı yer imlerinize ekleyin.

Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Google+ fotoğrafı

Google+ hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Connecting to %s