Python修飾器(三十八)

2019-07-30   IT技術院

裝飾器接收一個功能,添加一些功能並返回。 在本文中,您將學習如何創建裝飾器,以及為什麼要使用裝飾器。

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
******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%