Python修飾器


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

Python有一個有趣的功能,稱為裝飾器,以便為現有程式碼新增功能。

這也稱為超程式設計,作為程式的一部分,嘗試在編譯時修改程式的另一部分。

學習裝修器之前需要了解什麼?

為了瞭解裝飾器,我們首先在Python中了解一些基本的東西。

Python中的一切(是的,甚至是類)都是物件。 我們定義的名稱只是系結到這些物件的識別符號。 函式也不例外,它們也是物件(帶有屬性)。 各種不同的名稱可以繫結到同一個功能物件。

看看下面一個範例 -

def first(msg):
    print(msg)    

first("Hello")

second = first
second("Hello")

當執行程式碼時,firstsecond函式都提供相同的輸出。 這裡名稱firstsecond參照相同的函式物件。

函式可以作為引數傳遞給另一個函式。

如果您在Python中使用了mapfilterreduce等功能,那麼您就了解了。

將其他函式作為引數的函式也稱為高階函式。下面是這樣子的一個函式的例子。

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

該函式有兩個引數ab。 我們知道,如果將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
******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%