PYTHON 入門|函式的額外能力
Python Decorator 裝飾器:5 個簡單又經典的範例
裝飾器(Decorator)可以在不改動原函式主要程式碼的情況下,替它加上「執行前處理」或「執行後處理」。常見用途包括顯示訊息、記錄執行時間、檢查權限與保留函式資訊。
先記住一句話:裝飾器接收一個函式,回傳一個包裝函式;呼叫原函式時,就會先經過這層包裝。
一、裝飾器的運作流程
在函式定義上方寫 @my_decorator,Python 會把函式交給裝飾器處理。下面流程圖以「前後訊息」為例:
最常見的結構如下。*args 與 **kwargs 讓包裝函式可以接收不同數量的位置參數與關鍵字參數;return 則把原函式的結果傳回去。
def decorator(func):
def wrapper(*args, **kwargs):
# 額外處理:執行原函式之前
result = func(*args, **kwargs)
# 額外處理:執行原函式之後
return result
return wrapper
二、範例 1:在函式前後顯示訊息
def my_decorator(func):
def wrapper():
print("開始執行")
func()
print("執行結束")
return wrapper
@my_decorator
def say_hello():
print("Hello, Python!")
say_hello()
執行結果
開始執行
Hello, Python!
執行結束
程式解說
my_decorator(func)收到原本的say_hello函式。- 裝飾器建立
wrapper,在呼叫原函式前印出「開始執行」,呼叫後再印出「執行結束」。 return wrapper把包裝函式交回去,因此之後呼叫say_hello(),實際上執行的是 wrapper。@my_decorator是簡寫,等同於say_hello = my_decorator(say_hello)。
三、範例 2:讓裝飾器支援函式參數
def show_call(func):
def wrapper(*args, **kwargs):
print(f"正在呼叫:{func.__name__}")
return func(*args, **kwargs)
return wrapper
@show_call
def greet(name):
print(f"你好,{name}!")
greet("小明")
執行結果
正在呼叫:greet
你好,小明!
程式解說
*args收集位置參數,例如這裡的"小明";**kwargs收集像name="小明"這種關鍵字參數。func.__name__取得原函式名稱,方便記錄目前呼叫哪個函式。func(*args, **kwargs)再把收到的參數完整傳給原函式。return會保留原函式的回傳值;若少了它,呼叫端通常會收到None。
四、範例 3:計算函式執行時間
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 執行時間:{end - start:.4f} 秒")
return result
return wrapper
@timer
def count_numbers():
total = sum(range(1_000_000))
return total
print(count_numbers())
執行結果(時間會因電腦而不同)
count_numbers 執行時間:0.0284 秒
499999500000
程式解說
start記錄函式執行前的時間;原函式完成後用end記錄時間。end - start是執行所花的秒數,:.4f表示顯示到小數點後四位。- 原函式回傳總和,包裝函式將結果存在
result,印出時間後再回傳,因此外層的print仍能印出總和。 - 範例中的時間只是示意;實際數值會受電腦與當時負載影響。
五、範例 4:執行函式前檢查登入狀態
def login_required(func):
def wrapper(is_logged_in):
if not is_logged_in:
print("請先登入")
return
return func(is_logged_in)
return wrapper
@login_required
def view_profile(is_logged_in):
print("顯示個人資料")
view_profile(False)
view_profile(True)
執行結果
請先登入
顯示個人資料
程式解說
- wrapper 先檢查
is_logged_in的值。 - 若值是
False,印出提示後用return結束,不再執行原函式。 - 若值是
True,程式呼叫func(is_logged_in),於是顯示個人資料。 - 這是權限檢查的教學示範;實際網站應由登入系統與伺服器端驗證使用者身分。
六、範例 5:用 wraps 保留原函式資訊
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("執行前")
return func(*args, **kwargs)
return wrapper
@my_decorator
def say_hello():
"""向使用者打招呼"""
print("Hello!")
print(say_hello.__name__)
print(say_hello.__doc__)
say_hello()
執行結果
say_hello
向使用者打招呼
執行前
Hello!
程式解說
- 經過裝飾後,函式名稱可能被包裝層的名稱
wrapper取代,文件字串也可能不再指向原函式。 from functools import wraps匯入標準函式庫提供的工具。- 把
@wraps(func)放在 wrapper 定義上方,可以將原函式的名稱、說明文件等資訊保留下來。 - 這是撰寫一般裝飾器時很好的習慣,尤其當函式會被文件工具、除錯工具或其他程式檢查時。
七、快速整理
- 核心概念:裝飾器接收函式,並回傳包裝後的函式。
- 支援參數:多數通用裝飾器使用
*args、**kwargs。 - 保留回傳值:用
return func(...)把原函式的結果傳回。 - 保留函式資訊:使用
functools.wraps。 - 常見用途:日誌、計時、權限檢查、快取與輸入驗證。
熟悉這幾個例子後,可以試著把「執行前後顯示訊息」改成記錄每次函式呼叫的參數,或替計時裝飾器加上不同函式的測試。從 wrapper 的進入、原函式的執行,到結果回傳,便能掌握裝飾器的基本運作方式。
沒有留言:
張貼留言