Python Dekoratörler
Bir dekoratör bir işlevi alır, bazı işlevler ekler ve onu döndürür. Bu eğitimde, bir dekoratörü nasıl oluşturabileceğinizi ve onu neden kullanmanız gerektiğini öğreneceksiniz.
Python’da Dekoratörler
Python, mevcut bir koda işlevsellik eklemek için dekoratörler adı verilen ilginç bir özelliğe sahiptir .
Buna metaprogramlama da denir, çünkü programın bir kısmı derleme zamanında programın başka bir bölümünü değiştirmeye çalışır.
Dekoratörleri öğrenmek için ön koşullar
Dekoratörleri anlamak için önce Python’da birkaç temel şeyi bilmeliyiz.
Python’daki her şeyin (Evet! Sınıflar bile) nesneler olduğu konusunda rahat olmalıyız . Tanımladığımız isimler basitçe bu nesnelere bağlı tanımlayıcılardır. İşlevler istisna değildir, onlar da nesnelerdir (niteliklerle). Aynı işlev nesnesine çeşitli farklı adlar bağlanabilir.
İşte bir örnek.
1 2 3 4 5 6 7 8 | def first(msg): print(msg) first("Hello") second = first second("Hello") |
Çıktı
1 2 | <samp>Merhaba Merhaba</samp> |
Kodu çalıştırdığınızda hem çalışır hem first
de second
aynı çıktıyı verir. Burada isimler first
ve second
aynı fonksiyon nesnesine atıfta bulunur.
Şimdi işler daha da tuhaflaşmaya başladı.
Fonksiyonlar, başka bir fonksiyona argüman olarak iletilebilir.
Python’da ve gibi işlevleri kullandıysanız map
, bunu zaten biliyorsunuzdur.filter
reduce
Diğer işlevleri bağımsız değişken olarak alan bu tür işlevlere yüksek dereceli işlevler de denir . İşte böyle bir işlevin bir örneği.
1 2 3 4 5 6 7 8 9 10 11 | def inc(x): return x + 1 def dec(x): return x - 1 def operate(func, x): result = func(x) return result |
Fonksiyonu aşağıdaki gibi çağırıyoruz.
1 2 3 4 | >>> operate(inc,3) 4 >>> operate(dec,3) 2 |
Ayrıca, bir işlev başka bir işlevi döndürebilir.
1 2 3 4 5 6 7 8 9 10 | def is_called(): def is_returned(): print("Hello") return is_returned new = is_called() # Outputs "Hello" new() |
Çıktı
1 | <samp>Merhaba</samp> |
Burada, is_returned()
her çağırdığımızda tanımlanan ve döndürülen iç içe bir işlevdir is_called()
.
Son olarak, Python’daki C kayıpları hakkında bilgi sahibi olmalıyız .
Dekoratörlere geri dönmek
Fonksiyonlar ve metotlar çağrılabildiklerinden callable olarak adlandırılırlar.
Aslında, özel __call__()
yöntemi uygulayan herhangi bir nesne çağrılabilir olarak adlandırılır. Yani, en temel anlamda, bir dekoratör, çağrılabilir bir çağrı döndüren bir çağrılabilir.
Temel olarak, bir dekoratör bir işlevi alır, bazı işlevler ekler ve onu döndürür.
1 2 3 4 5 6 7 8 9 | def make_pretty(func): def inner(): print("I got decorated") func() return inner def ordinary(): print("I am ordinary") |
Aşağıdaki kodları Shell’de çalıştırdığınızda,
1 2 3 4 5 6 7 8 | >>> ordinary() I am ordinary >>> # let's decorate this ordinary function >>> pretty = make_pretty(ordinary) >>> pretty() I got decorated I am ordinary |
Yukarıda gösterilen örnekte make_pretty()
, bir dekoratördür. Atama adımında:
1 | pretty = make_pretty(ordinary) |
Fonksiyon ordinary()
dekore edilmiş ve döndürülen fonksiyona isim verilmiştir pretty
.
Dekoratör işlevinin orijinal işleve bazı yeni işlevler eklediğini görebiliriz. Bu bir hediyeyi paketlemeye benzer. Dekoratör bir sarıcı görevi görür. Süslenen nesnenin (içerideki gerçek hediye) niteliği değişmez. Ama şimdi güzel görünüyor (süslendiğinden beri).
Genel olarak, bir işlevi dekore eder ve onu şu şekilde yeniden atarız:
1 | ordinary = make_pretty(ordinary). |
Bu yaygın bir yapıdır ve bu nedenle Python’un bunu basitleştirmek için bir sözdizimi vardır.
Sembolü, dekoratör fonksiyonunun adıyla birlikte kullanabilir ve @
dekore edilecek fonksiyonun tanımının üstüne yerleştirebiliriz. Örneğin,
1 2 3 | @make_pretty def ordinary(): print("I am ordinary") |
eşdeğerdir
1 2 3 | def ordinary(): print("I am ordinary") ordinary = make_pretty(ordinary) |
Bu, dekoratörleri uygulamak için sadece sözdizimsel bir şekerdir.
Parametrelerle Dekorasyon Fonksiyonları
Yukarıdaki dekoratör basitti ve sadece herhangi bir parametresi olmayan fonksiyonlarla çalıştı. Ya aşağıdaki gibi parametreleri alan fonksiyonlarımız olsaydı:
1 2 | def divide(a, b): return a/b |
Bu fonksiyonun iki parametresi vardır,aveb. Geçersek hata vereceğini biliyoruz.b0 olarak
1 2 3 4 5 6 | >>> divide(2,5) 0.4 >>> divide(2,0) Traceback (most recent call last): ... ZeroDivisionError: division by zero |
Şimdi hataya neden olacak bu durumu kontrol etmek için bir dekoratör yapalım.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def smart_divide(func): def inner(a, b): print("I am going to divide", a, "and", b) if b == 0: print("Whoops! cannot divide") return return func(a, b) return inner @smart_divide def divide(a, b): print(a/b) |
Bu yeni uygulama None
, hata durumu ortaya çıkarsa geri dönecektir.
1 2 3 4 5 6 7 | >>> divide(2,5) I am going to divide 2 and 5 0.4 >>> divide(2,0) I am going to divide 2 and 0 Whoops! cannot divide |
Bu şekilde parametre alan fonksiyonları süsleyebiliriz.
Keskin bir gözlemci inner()
, dekoratörün içindeki iç içe işlevin parametrelerinin, süslediği işlevlerin parametreleriyle aynı olduğunu fark edecektir. Bunu dikkate alarak artık istediğimiz sayıda parametre ile çalışan genel dekoratörler yapabiliriz.
Python’da bu sihir şu şekilde yapılır function(*args, **kwargs)
. Bu şekilde, konumsal argümanların demetiargs
olacak ve anahtar kelime argümanlarının sözlüğü olacaktır . Böyle bir dekoratörün bir örneği şöyle olacaktır:kwargs
1 2 3 4 5 | def works_for_all(func): def inner(*args, **kwargs): print("I can decorate any function") return func(*args, **kwargs) return inner |
Python’da Dekoratörleri Zincirleme
Python’da birden fazla dekoratör zincirlenebilir.
Yani bir fonksiyon farklı (veya aynı) dekoratörlerle birden çok kez dekore edilebilir. Basitçe dekoratörleri istenen fonksiyonun üzerine yerleştiririz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | def star(func): def inner(*args, **kwargs): print("*" * 30) func(*args, **kwargs) print("*" * 30) return inner def percent(func): def inner(*args, **kwargs): print("%" * 30) func(*args, **kwargs) print("%" * 30) return inner @star @percent def printer(msg): print(msg) printer("Hello") |
Çıktı
1 2 3 4 5 | <samp>*********************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Merhaba %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ***********************************</samp> |
Yukarıdaki sözdizimi,
1 2 3 4 | @star @percent def printer(msg): print(msg) |
eşdeğerdir
1 2 3 | def printer(msg): print(msg) printer = star(percent(printer)) |
Dekoratörleri zincirleme sıramız önemlidir. Sıralamayı tersine çevirseydik,
1 2 3 4 | @percent @star def printer(msg): print(msg) |
Çıktı şöyle olacaktır:
1 2 3 4 5 | <samp>%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% *********************************** Merhaba *********************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%</samp> |