Python裝飾器詳解

2020-07-16 10:05:26
按照 Python 的程式設計原則,當一個函數被定義後,如要修改或擴充套件其功能應盡量避免直接修改函數定義的程式碼段,否則該函數在其他地方被呼叫時將無法正常執行。因此,當需要修改或擴充套件已被定義的函數的功能而不希望直接修改其程式碼時,可以使用裝飾器。

先來看一個簡單的例子:
def func1(function):
    print("這裡是執行function()函數之前")
    def wrapper():
        function()
    wrapper()
    print ("這裡是執行function ()函數之後")

@func1
def func2():
    print ("正在執行 function ()函數")

上述程式碼的執行結果如下所示:

>>> def func1(function):
...         print("這裡是執行function()函數之前")
...         def wrapper():
...             function()
...         wrapper()
...         print ("這裡是執行function()函數之後")

>>> @func1
def func2():
.    print ("正在執行 function ()函數")

   
這裡是執行function()函數之前
正在執行 function()函數
這裡是執行function()函數之後


這裡

@func1

等效於

func1(func2)


在 Python 中一切皆是物件,所以裝飾器本質上是一個返回函數的高階函數。結合之前介紹的關鍵字引數,可以將一個函數作為其外部函數的返回值,例如:
def func1(arg = True):
    def func2():
        print("This is func2() function")
    def func3():
        print("This is func3() function")
    if arg == True:
        return func2
    else :
        return func3

func1()()
上述程式碼的執行結果如下所示。

>>> def func1(arg = True):
...          def func2():
...              print("This is func2() function")
...          def func3():
...              print("This is func3() function")
...          if arg == True:
...              return func2
...          else :
...              return func3

>>> func1()()
This is func2() function


可以看到,呼叫 func1( ) 時實際上執行了 func2( ) 函數,第二對括號是用以執行 func2( ) 函數的,如果不寫這對括號,將只會得到 func2( ) 函數的參照資訊,如圖下所示。

>>> def func1(arg = True):
...          def func2():
...              print("This is func2() function")
...          def func3():
...              print("This is func3() function")
...          if arg == True:
...              return func2
...          else :
...              return func3

>>> func1()
<function func1.<locals>.func2 at 0x0000014ED8D663A0>


裝飾器也支援巢狀,巢狀的裝飾器的執行順序是從裡向外,最先呼叫最裡層的裝飾器,最後呼叫最外層的裝飾器,例如:

@a
@b
@c
def f()
pass


將按照以下順序執行:

f = a(b(c(f)))