裝飾器是一種特別的用法,可以在不改變原始函數的前提下新增內容
就像是在原始的內容下包了一層特殊的能力

裝飾器的本質

裝飾器其實就是一個可以接收函數作為參數的函數
Python是一個物件導向的程式語言,意即所有東西都是一個物件
就連函數也不例外

只要是一個物件就可以當成參數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def my_decorator(func):
def wrapper():
print("裝飾器正在執行一些事情...")
func()
print("裝飾器完成執行!")

return wrapper

@my_decorator
def say_hello():
print("Hello, world!")

say_hello()
# 裝飾器正在執行一些事情...
# Hello, world!
# 裝飾器完成執行!

在這裡,我們定義了一個my_decorator,並且拿它來裝飾say_hello
此時,say_hello會被當成一個參數傳進去my_decorator處理

my_decorator裡面,定義了一個新的函數wrapper,並且在wrapper執行了一些動作
最後把處理好的函數返回

你可以把裝飾器視為一個函數工廠
可以用固定化的格式產出一個wrapper函數,並且返回給你

最後,原本的say_hello就會被替換成含有wrapper的內容
你執行時不是執行原本的函數,而是執行裝飾完成後的函數

以下兩種寫法是一模一樣的意思
如同開頭提到的,裝飾器本質上是一個可以接收函數作為參數的函數
並且會返回一個修改後的函數

1
2
3
@my_decorator
def say_hello():
# 內容
1
2
3
4
def say_hello()
# 內容

say_hello = my_decorator(say_hello)

裝飾器的優點

如果你需要頻繁對多個函數進行同樣加工,此時裝飾器就非常有用
因為你不需要為每個函數都新增同樣的內容,只需要利用裝飾器就好了

就像我們寫機器人時使用的@bot.tree.command()
我們會需要大量新增指令,但我們不可能每一次都在函數裡加一大堆處理內容
這時使用裝飾器就能輕鬆解決

練習

以下是印出現在時間的方法

1
2
from datetime import datetime as dt
print(dt.now())

請製作一個裝飾器,可以印出函數開始執行與結束執行的時間