Python 簡易手冊

單元 48 - 裝飾子

如果函數 (function) 有額外的工作需要先執行,額外工作又可能是其他函數需要的,這個額外工作可以設計成以函數當參數 (parameter) ,然後設計內層函數,利用內層函數執行額外工作,內層函數的最後呼叫函數參數,外層函數再回傳內層函數,聽起來有沒有很複雜,先舉一個例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 定義執行共通工作的巢狀函數
def do_something(f):
    # 定義內層函數
    def do_something2():
        # 共通工作
        print(f.__name__)
        # 執行函數參數
        f()
    # 回傳內層函數
    return do_something2

# 定義函數
def do_something3():
    # 函數工作
    print("Something happened...")

# 呼叫定義共通工作的函數
func = do_something(do_something3)
# 執行回傳的函數
func()
# 印出空白行
print()
# 簡化呼叫方式
do_something(do_something3)()

# 檔名: def_demo15.py
# 說明: 《Python簡易手冊》的範例
# 網址: http://kaiching.org
# 作者: Kaiching Chang
# 時間: 2024 年 6 月

單元 44 - 函數與 return 陳述介紹如何定義函數。

注意共通工作定義在內層函數中,然後還要執行作為參數的函數

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 定義執行共通工作的巢狀函數
def do_something(f):
    # 定義內層函數
    def do_something2():
        # 共通工作
        print(f.__name__)
        # 執行函數參數
        f()
    # 回傳內層函數
    return do_something2

下面的 do_somehting3() 預計要先執行共通工作,這裡是印出函數名稱,先看到常規的呼叫方式

17
18
19
20
# 呼叫定義共通工作的函數
func = do_something(do_something3)
# 執行回傳的函數
func()

簡單說,要先以 do_something3 當作 do_something() 的引數 (argument) 並取得回傳值 (return value) ,由於回傳的是函數,因此還要在下一行執行回傳的函數。

第 24 行則是簡化呼叫 (call) 的方式,

23
24
# 簡化呼叫方式
do_something(do_something3)()

由於要以函數當引數,又要執行回傳的函數,因此需要在 do_something(do_something3) 的最後加上小括弧去執行,結果如下

> python def_demo15.py
do_something3
Something happened...
do_something3
Something happened...
>

以上的模式有很多小括弧,漏掉小括弧或是小括弧放錯位置都會導致出錯,這時候利用定義裝飾子 (decorator) 可以大幅簡化呼叫方式,例如

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 定義執行共通工作的巢狀函數
def do_something(f):
    # 定義內層函數
    def do_something2():
        # 共通工作
        print(f.__name__)
        # 執行函數參數
        f()
    # 回傳內層函數
    return do_something2

# 定義使用裝飾子的函數
@do_something
def do_something3():
    # 函數工作
    print("Something happened...")

# 呼叫函數
do_something3()

# 檔名: def_demo13.py
# 說明: 《Python簡易手冊》的範例
# 網址: http://kaiching.org
# 作者: Kaiching Chang
# 時間: 2024 年 3 月

這裡 do_something3() 函數定義的上一行加上 @do_something ,表示 @do_somethingdo_something3() 的裝飾子,因此會先去執行 do_something() 定義的共通工作,之後才會執行 do_something3() 定義的工作內容

12
13
14
15
16
# 定義使用裝飾子的函數
@do_something
def do_something3():
    # 函數工作
    print("Something happened...")

呼叫的時候就是直接呼叫 do_something3() ,少去接收回傳函數再執行回傳函數的過程

18
19
# 呼叫函數
do_something3()

此例執行結果如下

> python def_demo13.py
do_something3
Something happened...
>

如果需要執行的共通工作不止一個,裝飾子可以疊加上去,舉例如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 定義執行共通工作的巢狀函數
def do_something(f):
    # 定義內層函數
    def do_something2():
        # 共通工作
        print("1")
        # 執行函數參數
        f()
    # 回傳內層函數
    return do_something2

# 定義執行共通工作的巢狀函數
def do_something3(f):
    # 定義內層函數
    def do_something4():
        # 共通工作
        print("2")
        # 執行函數參數
        f()
    # 回傳內層函數
    return do_something4

# 定義使用裝飾子的函數
@do_something
@do_something3
def do_something5():
    # 函數工作
    print("Something happened...")

# 呼叫函數
do_something5()

# 檔名: def_demo14.py
# 說明: 《Python簡易手冊》的範例
# 網址: http://kaiching.org
# 作者: Kaiching Chang
# 時間: 2024 年 3 月

這裡用了兩個裝飾子,第一個裝飾子印出 1 ,第二個裝飾子印出 2

23
24
25
26
27
28
# 定義使用裝飾子的函數
@do_something
@do_something3
def do_something5():
    # 函數工作
    print("Something happened...")

如果不用裝飾子,定義這些共通工作的函數及呼叫方式就會非常複雜,因此裝飾子就是大幅簡化的語法糖,此例執行結果如下

> python def_demo14.py
1
2
Something happened...
>

參考資料

上一頁 單元 47 - 不限個數引數
回 Python 簡易手冊 首頁
下一頁 單元 49 - pass 陳述與省略符號 ...
回 Python 教材首頁
回程式語言教材首頁