Python装饰器疑问:无参数装饰器导入时为何未执行注册逻辑?
为什么两种装饰器写法在模块导入时表现不同?
咱们拆解下两种装饰器的执行逻辑,就能明白为啥导入时表现天差地别了。
第一种装饰器:注册逻辑没在导入阶段触发
你第一个版本的装饰器是接收函数作为参数的普通装饰器:
def register_scheduler(func): @functools.wraps(func) def func_wrapper(): print(f"adding {func.__name__}") schedule_registry.add(func) return func return func_wrapper
当用@register_scheduler装饰foo时,Python在导入模块时只会做一件事:foo = register_scheduler(foo)
这时候register_scheduler会返回func_wrapper这个函数对象,把它赋值给foo。但注意:func_wrapper里面的打印、集合添加逻辑,只有在你主动调用foo()的时候才会执行。模块导入阶段只是完成了“把foo替换成func_wrapper”这一步,根本没触发包装函数里的代码,所以自然没输出,集合也没被填充。
第二种装饰器:注册逻辑在装饰阶段直接执行
第二个版本的装饰器是无参装饰器工厂:
def register_scheduler(): def func_wrapper(func): print(f"adding {func.__name__}") schedule_registry.add(func) return func return func_wrapper
当用@register_scheduler()装饰foo时,Python的执行步骤变了:
- 先执行
register_scheduler(),返回内部定义的func_wrapper函数; - 紧接着用这个返回的
func_wrapper去处理foo,也就是执行foo = func_wrapper(foo)。
这时候func_wrapper里的打印和集合添加代码,是在**装饰应用的阶段(也就是模块导入时)**直接执行的——因为这一步是调用func_wrapper处理目标函数,而不是返回一个等着被调用的包装函数。所以导入模块时就会立刻打印"adding foo",集合也被正确填充了。
简单总结:
- 第一种装饰器返回的是“待调用的包装函数”,注册逻辑要等你调用原函数才触发;
- 第二种装饰器工厂先返回真正的装饰器,然后立刻用它处理目标函数,注册逻辑在模块加载时就跑完了。
内容的提问来源于stack exchange,提问作者AdamGold
相关产品推荐
相关产品推荐

