裝飾器是 Python 中一種強大的語法特性,它允許在不修改原函數代碼的情況下動態地擴展函數的功能。裝飾器本質上是一個高階函數,它接受一個函數作為參數并返回一個新的函數。
基本裝飾器
1. 簡單裝飾器示例
def my_decorator(func):def wrapper():print("函數執行前")func()print("函數執行后")return wrapper@my_decorator
def say_hello():print("Hello!")say_hello()
輸出:
函數執行前
Hello!
函數執行后
2. 裝飾器的工作原理
@my_decorator
語法糖等價于say_hello = my_decorator(say_hello)
my_decorator
接收say_hello
函數作為參數- 返回
wrapper
函數,它"包裹"了原函數
帶參數的裝飾器
1. 函數帶參數的裝飾器
def decorator_with_args(func):def wrapper(name):print(f"準備調用 {func.__name__}")func(name)print(f"{func.__name__} 調用完成")return wrapper@decorator_with_args
def greet(name):print(f"Hello, {name}!")greet("Alice")
2. 裝飾器本身帶參數
def repeat(num_times):def decorator(func):def wrapper(*args, **kwargs):for _ in range(num_times):func(*args, **kwargs)return wrapperreturn decorator@repeat(3)
def say_hello(name):print(f"Hello, {name}!")say_hello("Bob")
輸出:
Hello, Bob!
Hello, Bob!
Hello, Bob!
類裝飾器
裝飾器不僅可以是函數,還可以是類:
class CountCalls:def __init__(self, func):self.func = funcself.num_calls = 0def __call__(self, *args, **kwargs):self.num_calls += 1print(f"函數已被調用 {self.num_calls} 次")return self.func(*args, **kwargs)@CountCalls
def example():print("執行示例函數")example()
example()
example()
輸出:
函數已被調用 1 次
執行示例函數
函數已被調用 2 次
執行示例函數
函數已被調用 3 次
執行示例函數
多個裝飾器疊加
可以同時應用多個裝飾器:
def uppercase_decorator(func):def wrapper():result = func()return result.upper()return wrapperdef exclamation_decorator(func):def wrapper():result = func()return result + "!"return wrapper@exclamation_decorator
@uppercase_decorator
def say_hi():return "hi there"print(say_hi()) # 輸出: HI THERE!
注意:裝飾器的應用順序是從下往上,先應用 uppercase_decorator
,然后是 exclamation_decorator
。
內置裝飾器
Python 本身提供了一些內置裝飾器:
@staticmethod
- 定義靜態方法@classmethod
- 定義類方法@property
- 將方法轉換為屬性
裝飾器的常見用途
- 日志記錄
- 性能測試(計算函數執行時間)
- 權限驗證
- 緩存(備忘錄模式)
- 輸入驗證
- 注冊插件
- 重試機制
性能測試裝飾器示例
import timedef timer(func):def wrapper(*args, **kwargs):start = time.time()result = func(*args, **kwargs)end = time.time()print(f"{func.__name__} 執行時間: {end - start:.4f} 秒")return resultreturn wrapper@timer
def slow_function():time.sleep(2)slow_function()
保留原函數的元信息
使用 functools.wraps
可以保留原函數的元信息:
from functools import wrapsdef my_decorator(func):@wraps(func)def wrapper(*args, **kwargs):"""這是包裝函數的文檔字符串"""print("裝飾器添加的功能")return func(*args, **kwargs)return wrapper@my_decorator
def example():"""這是原函數的文檔字符串"""print("原函數功能")print(example.__name__) # 輸出: example
print(example.__doc__) # 輸出: 這是原函數的文檔字符串
總結
裝飾器是 Python 中非常強大的特性,它:
- 允許在不修改原函數代碼的情況下擴展功能
- 遵循開放-封閉原則(對擴展開放,對修改封閉)
- 使代碼更加模塊化和可重用
- 常用于日志、性能測試、權限控制等場景
掌握裝飾器可以讓你寫出更加優雅和高效的 Python 代碼。