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