![](../../img/interpret.jpg)
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_something 是 do_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... |
> |
參考資料
![](../../img/pydoing.gif)