在 Python 中,裝飾器(Decorator) 是一種設計模式,用于在不修改函數或類代碼的情況下動態地擴展其功能。裝飾器廣泛應用于日志記錄、性能監控、權限驗證等場景,提供了一種簡潔優雅的方式來“包裹”現有的代碼。本文將介紹裝飾器的基本概念、使用方式及常見場景。
1. 什么是裝飾器?
裝飾器是一個 高階函數,它接受一個函數或類作為輸入,并返回一個新的函數或類,通常用于增加額外的功能。通過裝飾器,我們可以在不修改原始代碼的情況下擴展函數或類的行為。裝飾器的核心思想是 “包裝”,它用來增強或修改目標函數或類的功能。
2. 裝飾器的基本語法
裝飾器的基本語法形式是:
@decorator_function
def target_function():pass
等價于:
def target_function():passtarget_function = decorator_function(target_function)
3. 創建一個簡單的函數裝飾器
函數裝飾器的實現通常包含兩個函數:一個是裝飾器本身,另一個是“包裝函數”。包裝函數會在原始函數執行前后執行自定義邏輯。
示例:簡單的日志裝飾器
def log_decorator(func):def wrapper(*args, **kwargs):print(f"Calling function: {func.__name__}")result = func(*args, **kwargs)print(f"Function {func.__name__} finished")return resultreturn wrapper@log_decorator
def say_hello(name):print(f"Hello, {name}!")say_hello("Alice")
輸出:
Calling function: say_hello
Hello, Alice!
Function say_hello finished
解釋:
log_decorator
是裝飾器,它接收say_hello
函數作為參數,返回一個新的函數wrapper
,wrapper
在執行say_hello
之前和之后打印日志。
4. 帶參數的裝飾器
裝飾器本身可以帶參數,這時需要增加額外的嵌套層級。這樣可以讓裝飾器在使用時更加靈活。
示例:帶參數的裝飾器
def repeat_decorator(repeat_count):def decorator(func):def wrapper(*args, **kwargs):for _ in range(repeat_count):result = func(*args, **kwargs)return resultreturn wrapperreturn decorator@repeat_decorator(repeat_count=3)
def say_hello(name):print(f"Hello, {name}!")say_hello("Alice")
輸出:
Hello, Alice!
Hello, Alice!
Hello, Alice!
解釋:
repeat_decorator
裝飾器可以接受一個repeat_count
參數,表示函數執行的次數。在wrapper
函數中,調用目標函數多次。
5. 類裝飾器
除了函數裝飾器,Python 也支持類裝飾器,用于修改或增強類的行為。
示例:類裝飾器
def add_method(cls):def new_method(self):print("This is a new method")cls.new_method = new_methodreturn cls@add_method
class MyClass:def greet(self):print("Hello!")obj = MyClass()
obj.greet() # 調用原方法
obj.new_method() # 調用新添加的方法
輸出:
Hello!
This is a new method
解釋:
add_method
裝飾器給MyClass
類添加了一個新的方法new_method
。通過@add_method
裝飾器,類MyClass
被修改,新增了一個方法。
6. 使用 functools.wraps
保持原函數屬性
當我們使用裝飾器時,目標函數的元數據(如函數名、文檔字符串等)會被包裝函數覆蓋。為了保留原函數的屬性,我們可以使用 functools.wraps
裝飾器。
示例:使用 wraps
保持元數據
from functools import wrapsdef log_decorator(func):@wraps(func)def wrapper(*args, **kwargs):print(f"Calling function: {func.__name__}")return func(*args, **kwargs)return wrapper@log_decorator
def say_hello(name):"""Say hello to someone."""print(f"Hello, {name}!")print(say_hello.__name__) # 輸出函數的名字
print(say_hello.__doc__) # 輸出函數的文檔字符串
輸出:
say_hello
Say hello to someone.
解釋:
- 使用
@wraps(func)
保證了裝飾器函數wrapper
保留了原始函數say_hello
的名字和文檔字符串。
7. 常見應用場景
7.1 緩存結果(Memoization)
裝飾器可以用于緩存函數的返回結果,避免重復計算,提升性能。
示例:緩存裝飾器
def memoize(func):cache = {}def wrapper(*args):if args not in cache:cache[args] = func(*args)return cache[args]return wrapper@memoize
def slow_function(x):print(f"Computing {x}...")return x * xprint(slow_function(5))
print(slow_function(5)) # 緩存值,不會重復計算
輸出:
Computing 5...
25
25
7.2 權限驗證
裝飾器常用于在執行函數前進行權限驗證或用戶身份檢查。
示例:權限驗證裝飾器
def requires_permission(func):def wrapper(user, *args, **kwargs):if not user.get("is_admin", False):raise PermissionError("User does not have permission")return func(user, *args, **kwargs)return wrapper@requires_permission
def delete_user(user, username):print(f"Deleting user {username}")user = {"name": "Alice", "is_admin": False}
delete_user(user, "Bob") # 拋出權限錯誤
8. 總結
裝飾器是 Python 中的一項強大功能,能夠讓我們以非常簡潔的方式在不改變原始代碼的情況下增加功能。它們不僅適用于函數,也可以用于類、方法等。裝飾器常常用于日志記錄、緩存、權限驗證等場景,在 Python 開發中十分常見。