装饰器工厂是返回装饰器的函数,它接收配置参数并利用闭包动态生成定制化装饰器;普通装饰器直接接收函数并返回新函数,而工厂需多一层调用(如@my_factory(arg=...))。

什么是装饰器工厂?它和普通装饰器有什么区别
装饰器工厂不是装饰器本身,而是一个返回装饰器的函数。它利用闭包捕获外部参数,在运行时动态生成定制化的装饰器。普通装饰器接收一个函数并返回新函数;装饰器工厂则接收配置参数(比如日志级别、重试次数),再返回那个“真正干活”的装饰器。
常见错误现象:TypeError: my_decorator() missing 1 required positional argument: 'func'——这往往是因为少写了一层调用,把工厂函数当成了装饰器直接用了。
- 工厂函数必须返回一个可调用对象(通常是内部定义的装饰器函数)
- 装饰器函数本身必须接收
func参数,并返回包装后的函数 - 调用时要写成
@my_factory(arg=...),而不是@my_factory
怎么写一个带参数的日志装饰器工厂
这个场景很典型:你想控制是否打印日志、日志前缀用什么、是否记录执行时间。用闭包把参数存进外层作用域,内层装饰器就能访问。
def log_calls(prefix="CALL", enabled=True):
def decorator(func):
def wrapper(*args, **kwargs):
if not enabled:
return func(*args, **kwargs)
print(f"[{prefix}] Calling {func.__name__}")
result = func(*args, **kwargs)
print(f"[{prefix}] Done {func.__name__}")
return result
return wrapper
return decorator
使用方式:
@log_calls(prefix="DEBUG", enabled=True)def hello(): print("hi")
注意点:
-
log_calls(...)先被调用,返回decorator函数 - 然后 Python 用这个
decorator去装饰hello - 如果漏掉括号(如
@log_calls),就会报错:Python 传给decorator的是hello,但log_calls并不接收函数作为参数
为什么需要用 functools.wraps?不加会出什么问题
不加 functools.wraps,被装饰函数的元信息(name、doc、module)会丢失,变成 wrapper 的信息。这对调试、文档生成、类型检查(如 inspect.signature)都会造成干扰。
修改上面的 wrapper 定义:
from functools import wraps
<p>def log_calls(prefix="CALL", enabled=True):
def decorator(func):
@wraps(func) # ← 关键一行
def wrapper(*args, <strong>kwargs):
if not enabled:
return func(*args, *<em>kwargs)
print(f"[{prefix}] Calling {func.<strong>name</strong>}")
result = func(</em>args, </strong>kwargs)
print(f"[{prefix}] Done {func.<strong>name</strong>}")
return result
return wrapper
return decorator</p>
性能影响几乎为零,但兼容性极重要——尤其当你写的装饰器要被别人复用,或集成进框架(如 FastAPI 的依赖注入)时,name 错乱会导致路由注册失败、依赖解析异常等隐蔽问题。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
装饰器工厂能接收哪些类型的参数?有没有不能传的
可以传任何 Python 对象:字符串、数字、布尔值、函数、类、甚至 None。但要注意两点:
- 参数必须在装饰器定义阶段就确定(即模块导入时或函数定义时),不能依赖运行时状态(比如不能传一个还没初始化的数据库连接实例,除非你确保工厂调用时它已就绪)
- 避免传可变对象(如
list、dict)作默认参数,否则所有使用该工厂生成的装饰器会共享同一份引用
示例危险写法:
def bad_factory(cache={}): # ← 所有装饰器共用一个 dict
正确做法:
def good_factory(cache=None): if cache is None: cache = {}
闭包捕获的是变量的引用,不是快照。如果传入的是一个会变化的对象(比如某个全局计数器),那后续每次调用装饰器时读到的都是最新值——这有时是想要的,有时是陷阱,得看设计意图。
闭包本身不复杂,但参数生命周期和绑定时机容易被忽略。写完先用 print 检查下外层函数的参数是不是真按你想的那样进了内层作用域。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










