递归函数赋值与环境使用困惑:斐波那契函数场景解析
问题:递归斐波那契函数重赋值后,递归调用为何指向新函数?
我正在学习《Composing Programs》中的优化知识,写了三个函数:memo实现记忆化,count是统计调用次数的包装器,fib是递归版斐波那契函数。
代码如下:
def memo(f): cache = {} def memoized(n): if n not in cache: cache[n] = f(n) return cache[n] return memoized def count(f): def counted(*args): counted.call_count += 1 return f(*args) counted.call_count = 0 return counted def fib(n): if n == 0: return 0 if n == 1: return 1 return fib(n - 2) + fib(n - 1)
调用时,我先把count(fib)赋值给counted_fib用来统计调用次数,之后又把fib变量重新赋值为memo(counted_fib),不再指向原函数。
调用fib(19)时一切正常,但原函数里的递归语句return fib(n - 2) + fib(n - 1)触发的却是memo(counted_fib)版本的fib。我想不通为什么会这样,毕竟原函数是在后续赋值之前定义的。
解答
核心原因是Python中函数内部的变量引用是动态查找的,不是在函数定义时就绑定到当时的对象上。
具体拆解整个过程:
- 原
fib函数定义时,里面的fib只是一个名称,并没有和定义时的递归函数体绑定死。当代码执行到return fib(n-2)+fib(n-1)这一行时,Python会去当前作用域(这里是全局作用域)查找名为fib的对象,而非直接使用定义时的那个函数。 - 执行
counted_fib = count(fib)时,counted_fib指向包装了原fib的统计函数,但此时全局的fib还是最初的递归函数。 - 当你执行
fib = memo(counted_fib)后,全局名称fib的指向彻底改变——现在它指向memo返回的记忆化包装函数,不再是最初定义的递归函数。 - 调用
fib(19)时,先进入记忆化函数,缓存未命中时会调用counted_fib(统计包装函数),统计函数又会调用它内部保存的原fib函数。此时原fib函数执行到递归语句,去全局查找fib,找到的就是已经被重赋值的memo(counted_fib),所以递归调用的是这个优化后的版本。
简单来说:原函数里的fib是个“活”的名称,每次使用时才会查找当前对应的对象,而非绑定定义时的固定函数。
内容的提问来源于stack exchange,提问作者Alejandro
相关产品推荐
相关产品推荐

