为何该递归Python代码在3.6正常运行,3.7却触发栈溢出?
问题描述
在script.py中存在如下代码:
def f(n, memo={0:0, 1:1}): if n not in memo: memo[n] = sum(f(n - i) for i in [1, 2]) return memo[n] print(f(400))
运行表现差异:
- 执行
python3.6 script.py时,能正确输出f(400)的结果,递归深度上限在调用f(501)时才会触发。 - 执行
python3.7 script.py时,直接发生栈溢出,递归深度上限在调用f(334)时就会触发。
问题:Python 3.6与3.7之间的哪些变化导致该代码更早达到递归深度上限?
原因解析
核心原因是Python 3.7对生成器表达式的栈帧管理逻辑做了修改:
- 在Python 3.6及更早版本中,
sum(f(n - i) for i in [1, 2])里的生成器表达式会复用当前函数f的栈帧,不会额外占用递归栈的层级。也就是说,调用f(n-1)和f(n-2)时,生成器本身不会让栈深度增加。 - 从Python 3.7开始,生成器表达式会创建独立的栈帧。这意味着每次执行这个生成器时,都会多占一层栈空间。原本计算
f(n)只需要两次递归调用的栈深度,现在因为生成器的额外栈帧,实际递归深度相当于被“翻倍”了——所以3.6里能支撑到500左右的递归层级,3.7里就只能支撑约一半的数值(334),更早触发RecursionError。
可以通过修改代码验证这个结论:把生成器表达式换成直接的加法运算f(n-1) + f(n-2),修改后的代码在3.6和3.7中都能正常运行f(400),触发递归上限的数值也会基本一致。修改后的代码如下:
def f(n, memo={0:0, 1:1}): if n not in memo: memo[n] = f(n-1) + f(n-2) return memo[n] print(f(400))
这个调整是Python官方在3.7版本中为了优化生成器的调试体验和异常追踪能力做出的,但也带来了栈帧开销的增加,间接影响了递归深度的表现。
内容的提问来源于stack exchange,提问作者wim
相关产品推荐
相关产品推荐

