裝飾器接收一個(gè)功能,添加一些功能并返回。 在本文中,您將學(xué)習(xí)如何創(chuàng)建裝飾器,以及為什么要使用裝飾器。
Python有一個(gè)有趣的功能,稱為裝飾器,以便為現(xiàn)有代碼添加功能。
這也稱為元編程,作為程序的一部分,嘗試在編譯時(shí)修改程序的另一部分。
為了了解裝飾器,我們首先在Python中了解一些基本的東西。
Python中的一切(是的,甚至是類)都是對象。 我們定義的名稱只是綁定到這些對象的標(biāo)識符。 函數(shù)也不例外,它們也是對象(帶有屬性)。 各種不同的名稱可以綁定到同一個(gè)功能對象。
看看下面一個(gè)示例 -
def first(msg):
print(msg)
first("Hello")
second = first
second("Hello")
當(dāng)運(yùn)行代碼時(shí),first和second函數(shù)都提供相同的輸出。 這里名稱first和second引用相同的函數(shù)對象。
函數(shù)可以作為參數(shù)傳遞給另一個(gè)函數(shù)。
如果您在Python中使用了map,filter和reduce等功能,那么您就了解了。
將其他函數(shù)作為參數(shù)的函數(shù)也稱為高階函數(shù)。下面是這樣子的一個(gè)函數(shù)的例子。
def inc(x):
return x + 1
def dec(x):
return x - 1
def operate(func, x):
result = func(x)
return result
我們調(diào)用函數(shù)如下 -
>>> operate(inc,3)
4
>>> operate(dec,3)
2
此外,一個(gè)函數(shù)可以返回另一個(gè)函數(shù)。
def is_called():
def is_returned():
print("Hello")
return is_returned
new = is_called()
#Outputs "Hello"
new()
這里,is_returned()是一個(gè)定義的嵌套函數(shù),在每次調(diào)用is_called()時(shí)返回。
實(shí)際上,實(shí)現(xiàn)特殊方法__call__()的任何對象都被稱為可調(diào)用。 因此,在最基本的意義上,裝飾器是可調(diào)用的,并且可以返回可調(diào)用。
基本上,裝飾器接收一個(gè)函數(shù),添加一些函數(shù)并返回。
def make_pretty(func):
def inner():
print("I got decorated")
func()
return inner
def ordinary():
print("I am ordinary")
當(dāng)在shell中運(yùn)行以下代碼時(shí),如下 -
>>> ordinary()
I am ordinary
>>> # let's decorate this ordinary function
>>> pretty = make_pretty(ordinary)
>>> pretty()
I got decorated
I am ordinary
在上面的例子中,make_pretty()是一個(gè)裝飾器。 在分配步驟。
pretty = make_pretty(ordinary)
函數(shù)ordinary()得到了裝飾,返回函數(shù)的名字:pretty。
可以看到裝飾函數(shù)為原始函數(shù)添加了一些新功能。這類似于包裝禮物。 裝飾器作為包裝紙。 裝飾物品的性質(zhì)(里面的實(shí)際禮物)不會(huì)改變。 但現(xiàn)在看起來很漂亮(因?yàn)檠b飾了)。
一般來說,我們裝飾一個(gè)函數(shù)并重新分配它,
ordinary = make_pretty(ordinary).
這是一個(gè)常見的結(jié)構(gòu),Python有一個(gè)簡化的語法。
可以使用@符號和裝飾器函數(shù)的名稱,并將其放在要裝飾的函數(shù)的定義之上。 例如,
@make_pretty
def ordinary():
print("I am ordinary")
上面代碼相當(dāng)于 -
def ordinary():
print("I am ordinary")
ordinary = make_pretty(ordinary)
上面的裝飾器很簡單,只適用于沒有任何參數(shù)的函數(shù)。 如果有函數(shù)要接受如下的參數(shù)怎么辦?
def divide(a, b):
return a/b
該函數(shù)有兩個(gè)參數(shù)a和b。 我們知道,如果將b的值設(shè)置為0并傳遞那么是會(huì)出錯(cuò)的。
>>> divide(2,5)
0.4
>>> divide(2,0)
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
現(xiàn)在使用一個(gè)裝飾器來檢查這個(gè)錯(cuò)誤。
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):
return a/b
如果發(fā)生錯(cuò)誤,這個(gè)新的實(shí)現(xiàn)將返回None。
>>> 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
以這種方式就可以裝飾函數(shù)的參數(shù)了。
應(yīng)該會(huì)注意到,裝飾器中嵌套的inner()函數(shù)的參數(shù)與其裝飾的函數(shù)的參數(shù)是一樣的。 考慮到這一點(diǎn),現(xiàn)在可以讓一般裝飾器使用任何數(shù)量的參數(shù)。
在Python中,這個(gè)由function(* args,** kwargs)完成。 這樣,args將是位置參數(shù)的元組,kwargs將是關(guān)鍵字參數(shù)的字典。這樣的裝飾器的例子將是。
def works_for_all(func):
def inner(*args, **kwargs):
print("I can decorate any function")
return func(*args, **kwargs)
return inner
多個(gè)裝飾器可以在Python中鏈接。
這就是說,一個(gè)函數(shù)可以用不同(或相同)裝飾器多次裝飾。只需將裝飾器放置在所需函數(shù)之上。
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")
執(zhí)行上面代碼,將輸出結(jié)果如下 -
******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Hello
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************
以上語法,
@star
@percent
def printer(msg):
print(msg)
相當(dāng)于以下 -
def printer(msg):
print(msg)
printer = star(percent(printer))
鏈裝飾器的順序是重要的。 所以如果把順序顛倒了執(zhí)行結(jié)果就不一樣了,如下 -
@percent
@star
def printer(msg):
print(msg)
執(zhí)行上面代碼,將輸出結(jié)果如下 -
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************
Hello
******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%