带参数装饰器本质是三层嵌套函数:最外层接收装饰器参数,中间层接收被装饰函数,最内层wrapper执行逻辑;需确保最外层返回中间层函数对象。

装饰器带参数的本质是“三层函数”
带参数的装饰器不是直接修饰函数,而是先接收参数,返回一个真正的装饰器。这需要三层嵌套:
- 最外层:接收装饰器的参数(如 @log(level="INFO") 中的 level)
- 中间层:返回的装饰器,接收被装饰的函数(func)
- 最内层:实际执行逻辑的包装函数(通常叫 wrapper),负责调用原函数并添加额外行为
关键点:最外层函数必须返回中间层函数对象(不能直接返回 wrapper),否则语法会报错。
写一个可配置的日志装饰器
例如,想控制日志级别和是否启用:
def log(level="INFO", enabled=True):
def decorator(func):
def wrapper(*args, **kwargs):
if enabled:
print(f"[{level}] Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
return decorator
<p>@log(level="DEBUG", enabled=True)
def greet(name):
return f"Hello, {name}"
调用 greet("Alice") 会输出 [DEBUG] Calling greet,再返回结果。注意:参数在装饰时就已绑定,运行时不可变。
立即学习“Python免费学习笔记(深入)”;
多个装饰器叠加的执行顺序
当函数上方有多个装饰器(如 @A @B @C),Python 按从下到上顺序应用:
- 先用 C 包装原函数
- 再用 B 包装 C 的结果
- 最后用 A 包装 B 的结果
即等价于 A(B(C(func)))。所以最靠近函数的装饰器最先执行(进入时),最外层的装饰器最后执行(但退出时最先拦截返回值)。
保留原函数元信息的小技巧
用 @functools.wraps(func) 修饰 wrapper,能自动复制 __name__、__doc__ 等属性:
from functools import wraps
<p>def log(level="INFO"):
def decorator(func):
@wraps(func) # 关键!
def wrapper(*args, *<em>kwargs):
print(f"[{level}] {func.<strong>name</strong>} start")
result = func(</em>args, **kwargs)
print(f"[{level}] {func.<strong>name</strong>} done")
return result
return wrapper
return decorator
这样 help(greet) 就能正确显示原函数的文档字符串,而不是 wrapper 的。










