Python 中如何创建函数装饰器(并叠加多个装饰器)?
Python 装饰器是包装函数的高阶函数:定义一个接收函数、返回包装函数的函数,用 @语法应用。多个装饰器从下往上依次包装。本文给出完整教程。
装饰器本质:一个接收函数、返回新函数的函数。用 @装饰器名 放在目标函数上面即可应用。多个装饰器从下往上依次包装。
创建装饰器
import functools
def my_decorator(func):
@functools.wraps(func) # 保留原函数的元信息
def wrapper(*args, **kwargs):
print("函数调用前")
result = func(*args, **kwargs)
print("函数调用后")
return result
return wrapper
@my_decorator
def greet(name):
print(f"你好, {name}")
greet("张三")
# 输出: 函数调用前 → 你好, 张三 → 函数调用后
@functools.wraps(func) 很重要:保留原函数的 __name__、__doc__,否则调试和文档工具会拿到 wrapper 的信息。
带参数的装饰器
再加一层函数接收参数:
import time
import functools
def timing(unit="s"):
if unit not in ("s", "ms"):
raise ValueError("unit 必须为 s 或 ms")
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
try:
return func(*args, **kwargs)
finally:
elapsed = time.perf_counter() - start
if unit == "ms":
elapsed *= 1000
print(f"{func.__name__} 耗时 {elapsed:.4f}{unit}")
return wrapper
return decorator
@timing(unit="ms")
def slow_func():
time.sleep(0.5)
多个装饰器叠加
@decorator_a
@decorator_b
def func():
pass
# 等价于 func = decorator_a(decorator_b(func))
# 若各包装器调用被包装函数且正常返回,执行顺序:a 的前置 → b 的前置 → func → b 的后置 → a 的后置
常见问题(FAQ)
Q:装饰器能用于类方法吗?
A:能,wrapper 的第一个参数会收到 self,用 *args, **kwargs 透传即可。
Q:装饰类本身怎么做?
A:装饰器同样可以接收类并返回修改后的类:@dataclass 就是典型例子。
Q:如何写"可移除"的装饰器?
A:functools.wraps 会把原函数存到 __wrapped__ 属性,func.wrapped 只解开一层;多层可用 inspect.unwrap()。上面的同步计时包装器不适合计量 async 函数执行,异步版本需在 async wrapper 中 await。