Python中闭包与装饰器的区别:阶乘缓存实现效果差异解析
两种实现出现输出差异的根本原因
差异本质来自函数调用链路的绑定对象不同,和缓存逻辑本身的写法无关:
- 闭包实现的场景下,全局作用域里的
factorial始终是没有缓存能力的原始函数。你在run_closure中执行f = cache(factorial)时,只是创建了一个带缓存逻辑的局部包装对象f,并没有修改全局factorial的绑定。 - 当你调用f(5)时,缓存wrapper发现5不在缓存表,就会执行传入的原始
func(也就是没包装过的factorial)计算结果。这个原始factorial内部的递归逻辑return n * factorial(n - 1),找的是全局作用域下的原始factorial,完全不会经过你写的缓存wrapper。 - 整个计算过程中,只有你手动调用f()的路径会经过缓存判断,递归计算的4!、3!、2!这些中间值,根本没有机会被写入缓存表。缓存表里最终只会存入你手动通过f调用的两个值:第一次调用f(5)时存入的5!结果,第二次调用f(4)时临时计算并存入的4!结果,自然不会出现中间值的缓存命中。
补个验证:如果在闭包实现里,在f = cache(factorial)之后手动加上一句global factorial; factorial = f,把全局的factorial绑定也替换成带缓存的wrapper,这时候递归路径就会完全走缓存逻辑,最终输出就和装饰器实现完全一致了。
而装饰器实现能达到预期效果,是因为:
- Python执行
@cache语法的等价逻辑是factorial = cache(factorial),这一步直接把全局作用域下的factorial标识符,从原始函数替换成了带缓存的wrapper。 - 这时候factorial内部递归调用的
factorial(n-1),匹配到的就是全局作用域下的wrapper,每一层递归都会触发缓存判断,计算出的0!、1!、2!……直到5!的所有结果,都会被存入缓存表,后续的重复调用自然可以直接命中缓存。
闭包与装饰器的核心区别
- 闭包是Python的语法特性:指的是内部函数可以捕获并持有外部函数作用域的局部变量,哪怕外部函数执行完毕返回了,这些被捕获的变量依然可以被内部函数访问。它是实现带状态函数、缓存逻辑的基础能力,本身没有强制的执行规则。
- 装饰器是基于闭包实现的设计模式:它的核心约定是在不修改原函数源码、不改变原函数调用方式的前提下,用包装后的增强函数,替换原函数在原作用域的标识符绑定,从而让所有原函数的调用路径,都能自动触发附加的增强逻辑。
内容的提问来源于stack exchange,提问作者cmjdxy
相关产品推荐
相关产品推荐

