装饰器无需显式调用即可注册函数的原理探究
搞懂装饰器的执行时机:你误解的不是特例,是Python的底层规则
首先得给你纠正一个关键认知:装饰器根本不是在函数显式调用时才生效的,而是在函数定义的那一刻就执行了——这是Python装饰器的核心工作机制,不是什么特例。
先拆解你代码里的逻辑
你写的@register其实是Python的语法糖,等价于:
def say_hello(name): return f"Hello {name}" say_hello = register(say_hello)
看到没?当Python解释器加载你的模块、执行到def say_hello这部分代码时,它会先定义这个函数,然后立刻调用register(say_hello),把刚定义的函数对象传进去,再把register的返回值重新赋值给say_hello。
你的register函数做的事情很简单:把传入的函数对象存到PLUGINS字典里,然后返回原函数。所以不管你之后调不调用say_hello(),这个注册动作在函数定义时就完成了——这就是为什么PLUGINS里已经有这两个函数的原因。
如果register调用了func会怎样?
假设你把register改成这样:
def register(func): """Register a function as a plug-in""" PLUGINS[func.__name__] = func # 新增:调用传入的函数 print(func("测试用户")) return func
那当模块加载时,Python执行到@register修饰的say_hello和be_awesome时,会立刻调用这两个函数,输出:
Hello 测试用户 Yo 测试用户, together we are the awesomest!
这个时候你甚至还没调用randomly_greet,函数就已经被执行了——因为装饰器的执行时机是函数定义阶段,不是调用阶段。
这不是特例,是所有装饰器的通用规则
不管你写的装饰器是用来注册函数、做日志、加缓存还是什么其他功能,只要用了@装饰器的语法,装饰器函数都会在函数定义时被执行一次,处理被装饰的函数对象。
你之前的误解可能是把装饰器和函数调用混淆了:装饰器是用来“包装”或“处理”函数本身的,而不是等待函数被调用时才触发——函数调用是另一回事,那是你之后主动调用say_hello("Alice")的时候才会执行函数内部的逻辑。
内容的提问来源于stack exchange,提问作者Ivan

