Advanced Functions, Decorators, Nested Functions

Kategori: Python , 06 Eylül 2019 , JanFranco


Bu yazımda iç içe fonksiyonlardan, fonksiyonları argüman olarak göndermekten ve decorator fonksiyonlardan bahsedeceğim. İlk olarak iç içe fonksiyonlar ve fonksiyonların return edilmesi ile başlayalım.
math adında bir fonksiyon oluşturalım. Fonksiyon parametre olarak bir fonksiyon ismi alsın. Kullanıcı burada "add", "mult" gibi operasyonlar gönderebilir. math fonksiyonun içinde add ve mult fonksiyonları oluşturalım. Bu fonksiyonların parametrelerini *args olarak belirleyelim ki parametre serisi gönderilebilsin. Daha sonra bir if döngüsü açalım ve parametredeki fonksiyon ismine göre bir fonksiyon return edilsin:


def math(func_name):
    def add(*args):
        sum = 0
        for i in args:
            sum += i
        return sum

    def mult(*args):
        sum = 1
        for i in args:
            sum *= i
        return sum

    if func_name == "add":
        return add
    else:
        return mult
Bu fonksiyonu kullanalım:


my_func = math("add")
print(my_func(1, 2, 3, 4, 5))
my_func = math("mult")
print(my_func(1, 2, 3, 4, 5))
>>
15
120
Fonksiyonları return etmeyi gördük. Şimdi fonksiyonları argüman olarak göndermeyi deneyelim. add, diff, mult ve div adında 4 fonksiyon oluşturalım. Sonrasında math adında bir fonksiyon oluşturalım ve bu fonksiyon parametre olarak 4 fonksiyon ve bir operasyon ismi alsın. Operasyon ismine göre parametre olarak verilen fonksiyonlardan biri çağırılsın:


def add(a, b):
    return a + b
def diff(a, b):
    return a - b
def mult(a, b):
    return a * b
def div(a, b):
    return a // b

def math(func1, func2, func3, func4, op_name):
    if op_name == "add":
        print(func1(3, 4))
    elif op_name == "diff":
        print(func2(10, 3))
    elif op_name == "mult":
        print(func3(10,3))
    elif op_name == "div":
        print(func4(10,4))
    else:
        print("Error.")
math fonksiyonunu kullanalım:


math(add, diff, mult, div, "add")
math(add, diff, mult, div, "diff")
math(add, diff, mult, div, "mult")
math(add, diff, mult, div, "div")
>>
7
7
30
2
30 farklı fonksiyon yazdığımızı düşünelim. Daha sonra bu fonksiyonların hepsine bir özellik eklemek isteyelim, log kaydı veya zaman hesaplaması gibi bir özellik. Tek tek 30 farklı fonksiyonu değiştirmek yerine tek bir decorator fonksiyon yazarak bu işi çözebiliriz. Decorator fonksiyonlar kod tekrarını büyük bir ölçüde engeller. Örnek üzerinden anlatmak gerekirse, kareleri ve küpleri hesaplayıp listeye atan iki adet fonksiyon yazalım:


def calculate_squares(num):
    res = []
    for i in num:
        res.append(i ** 2)
    return res

def calculate_cubes(num):
    res = []
    for i in num:
        res.append(i ** 3)
    return res
Şimdi zaman hesabını yapacağımız decorator fonksiyonu yazalım:


def calculate_time(func):
    def wrapper(num):
        start = time.time()
        res = func(num)
        end = time.time()

        print(func.__name__ + " took " + str(end - start) + " secs.")

        return res
    return wrapper
Burada calculate_time adında bir fonksiyon oluşturduk. Parametre olarak bir fonksiyon belirledik. Bu fonksiyonun içinde wrapper adında bir fonksiyon oluşturduk. Wrapper fonksiyonun parametresini, daha önce yazdığımız fonksiyonların parametresi olan num olarak belirledik ancak farklı bir isim de verebilirdik. Zaman hesaplaması için res = func(num) komutunun öncesine ve sonrasına time.time() methodları ekledik. Daha sonrasında aradaki süreyi çıkararak kaç saniye sürdüğünü print ile konsola bastırdık. Son olarak sonucu yani res değişkenini ve wrapper fonksiyonu return ettik.
Şimdi kare ve küpleri hesaplayan fonksiyonları decorator fonksiyon ile etkileşime sokalım:


@calculate_time
def calculate_squares(num):
    res = []
    for i in num:
        res.append(i ** 2)
    return res


@calculate_time
def calculate_cubes(num):
    res = []
    for i in num:
        res.append(i ** 3)
    return res
Başlarına @calculate_time şeklinde bir komut verdik. Tek bir fonksiyon değil birden fazla decorator fonksiyon kullanabilirdik. Burada @calculate_time komutundan dolayı calculate_squares ve calculate_cubes fonksiyonları, calculate_time fonksiyonuna gönderildi ve bu iki fonksiyona gönderilen parametreler wrapper fonksiyonuna direk olarak aktarıldı. Şimdi fonksiyonları kullanalım:


calculate_squares(range(100000))
calculate_cubes(range(100000))
>>
calculate_squares took 0.07095503807067871 secs.
calculate_cubes took 0.07994341850280762 secs.
Tüm kodlar:


import time


def calculate_time(func):
    def wrapper(num):
        start = time.time()
        res = func(num)
        end = time.time()

        print(func.__name__ + " took " + str(end - start) + " secs.")

        return res
    return wrapper


@calculate_time
def calculate_squares(num):
    res = []
    for i in num:
        res.append(i ** 2)
    return res


@calculate_time
def calculate_cubes(num):
    res = []
    for i in num:
        res.append(i ** 3)
    return res


calculate_squares(range(100000))
calculate_cubes(range(100000))
Eğer bir talep gelirse decorator fonksiyonlar ile ilgili çok daha ayrıntılı ayrı bir yazı hazırlayabilirim.


Sonraki Yazı: Iterator
Yorumlar

Henüz bir yorum bulunmuyor.
Yorum bırakın