Python修飾器
裝飾器接收一個功能,添加一些功能並返回。 在本文中,您將學習如何創建裝飾器,以及爲什麼要使用裝飾器。
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中鏈接裝飾器
多個裝飾器可以在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
******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%