Bir dekoratör bir işlevi alır, bazı işlevler ekler ve onu döndürür. Bu eğitimde, nasıl bir dekoratör yaratabileceğ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 .
Bu aynı zamanda metaprogramlama olarak da adlandırılır, çünkü programın bir bölümü 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) nesne olduğu gerçeğiyle rahat olmalıyız. Tanımladığımız isimler, bu nesnelere bağlı basit tanımlayıcılardır. Fonksiyonlar istisna değildir, onlar da nesnelerdir (özniteliklerle). Aynı işlev nesnesine çeşitli farklı adlar bağlanabilir.
İşte bir örnek.
def first(msg): print(msg) first("Hello") second = first second("Hello")
Çıktı
Merhaba Merhaba
Eğer kod, her iki fonksiyonu çalıştırdığınızda first
ve second
aynı çıktıyı verir. Burada isimler first
ve second
aynı işlev nesnesine atıfta bulunur.
Şimdi işler daha da tuhaflaşmaya başlıyor.
Fonksiyonlar, başka bir işleve argüman olarak aktarılabilir.
Eğer gibi kullanılan fonksiyonlara varsa map
, filter
ve reduce
Python, o zaman zaten bunu biliyor.
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şleve bir örnek.
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.
>>> operate(inc,3) 4 >>> operate(dec,3) 2
Ayrıca, bir işlev başka bir işlevi döndürebilir.
def is_called(): def is_returned(): print("Hello") return is_returned new = is_called() # Outputs "Hello" new()
Çıktı
Merhaba
Burada, is_returned()
her çağırdığımızda tanımlanan ve dönen iç içe geçmiş bir işlev var is_called()
.
Son olarak, Python'daki Kapanışlar hakkında bilgi sahibi olmalıyız.
Dekoratörlere Geri Dönmek
İşlevler ve yöntemler, çağrılabildikleri için çağrılabilir olarak adlandırılır.
Aslında, özel __call__()
yöntemi uygulayan herhangi bir nesne çağrılabilir olarak adlandırılır. Bu nedenle, en temel anlamda, bir dekoratör, bir çağrılabilir döndüren bir çağrılabilirdir.
Temel olarak, bir dekoratör bir işlevi alır, bazı işlevler ekler ve onu döndürür.
def make_pretty(func): def inner(): print("I got decorated") func() return inner def ordinary(): print("I am ordinary")
Aşağıdaki kodları kabukta çalıştırdığınızda,
>>> 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:
pretty = make_pretty(ordinary)
İşlev ordinary()
dekore edildi ve döndürülen işleve ad verildi pretty
.
Dekoratör işlevinin orijinal işleve bazı yeni işlevler eklediğini görebiliriz. Bu, hediye paketlemeye benzer. Dekoratör bir sarmalayıcı görevi görür. Süslenen nesnenin doğası (içindeki gerçek hediye) değişmez. Ama şimdi güzel görünüyor (dekore edildiğinden beri).
Genel olarak, bir işlevi dekore ederiz ve şu şekilde yeniden atarız:
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 işlevinin adı ile birlikte kullanabilir ve onu dekore edilecek işlevin tanımının üstüne yerleştirebiliriz. Örneğin,
@make_pretty def ordinary(): print("I am ordinary")
eşdeğerdir
def ordinary(): print("I am ordinary") ordinary = make_pretty(ordinary)
This is just a syntactic sugar to implement decorators.
Decorating Functions with Parameters
The above decorator was simple and it only worked with functions that did not have any parameters. What if we had functions that took in parameters like:
def divide(a, b): return a/b
This function has two parameters, a and b. We know it will give an error if we pass in b as 0.
>>> divide(2,5) 0.4 >>> divide(2,0) Traceback (most recent call last):… ZeroDivisionError: division by zero
Now let's make a decorator to check for this case that will cause the error.
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)
This new implementation will return None
if the error condition arises.
>>> 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
In this manner, we can decorate functions that take parameters.
A keen observer will notice that parameters of the nested inner()
function inside the decorator is the same as the parameters of functions it decorates. Taking this into account, now we can make general decorators that work with any number of parameters.
In Python, this magic is done as function(*args, **kwargs)
. In this way, args
will be the tuple of positional arguments and kwargs
will be the dictionary of keyword arguments. An example of such a decorator will be:
def works_for_all(func): def inner(*args, **kwargs): print("I can decorate any function") return func(*args, **kwargs) return inner
Chaining Decorators in Python
Multiple decorators can be chained in Python.
This is to say, a function can be decorated multiple times with different (or same) decorators. We simply place the decorators above the desired function.
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")
Output
****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Hello %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ******************************
The above syntax of,
@star @percent def printer(msg): print(msg)
is equivalent to
def printer(msg): print(msg) printer = star(percent(printer))
The order in which we chain decorators matter. If we had reversed the order as,
@percent @star def printer(msg): print(msg)
The output would be:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ****************************** Hello ****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%