Python装饰器工作原理疑问:为何未出现预期的无限循环?
为什么你的代码没有触发无限循环?先搞懂装饰器的本质
哈哈,这个问题刚好戳中了装饰器最核心的逻辑——你误解了装饰器执行后函数的指向变化!咱们一步步拆解你的代码,就能明白为啥没出现无限循环了。
第一步:装饰器的“偷偷替换”动作
当你在salut()函数上方加@deco的时候,Python在代码运行阶段会自动执行一个隐藏操作:
salut = deco(salut)
划重点:你最初写的那个salut函数(就是打印"salut"的那个),被当成参数传给了deco;而deco返回的coucou函数,会直接覆盖掉原来的salut变量!
现在全局作用域里的salut,已经不是你最初定义的那个函数了——它现在指向的是coucou。而原来的那个原始salut,被保存在coucou的闭包中,也就是deco函数里的f参数,这个f会一直指向未被装饰的原始salut。
第二步:执行hi()的完整流程
当你调用hi()时,它会执行return salut()——这里的salut已经是被替换后的coucou了,所以完整流程是:
- 进入
coucou(),先打印"this is function{}".format(f)(这里的f是原始的salut函数对象) - 然后执行
return f()——注意!这里调用的是闭包里的f,也就是原始的、未被装饰的salut,它只会打印"salut",不会再触发装饰器逻辑,自然不会无限循环。
什么时候才会触发无限循环?
如果你的coucou函数里写的是调用salut()而不是f(),那才会陷入无限循环:
def deco(f): def coucou(): print("this is function{}".format(f)) return salut() # 这里调用的是已经被替换成coucou的salut return coucou
这时候,coucou里调用salut()其实就是调用它自己,会反复触发coucou的执行,直到栈溢出报错。
装饰器的核心总结
- 装饰器本质是函数替换:用装饰器返回的新函数,直接替换掉原来的被装饰函数
- 闭包保存原始函数:装饰器内部的新函数(比如
coucou)通过闭包持有原始函数的引用,这样才能在不循环的前提下调用原始逻辑 - 装饰器的核心价值:在不修改原始函数代码的前提下,给它增加额外功能(比如日志、计时、权限校验等)
内容的提问来源于stack exchange,提问作者Newgate Ace
相关产品推荐
相关产品推荐

