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