You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python推导式是否后台调用函数?递归栈差异原因探究

列表推导式与普通循环的递归栈差异问题解析

问题场景

首先定义递归阶乘函数:

def factorial(n):
    if n == 0:
        return n
    else:
        return n*factorial(n-1)

接着定义测试递归栈限制的test_recursion函数(已导入sys模块),返回触发RecursionError的最小num值:

def test_recursion(n):
    sys.setrecursionlimit(n)
    num = 0
    while True:
        try:
            x = factorial(num)
            num += 1
        except:
            return num

分别通过普通for循环和列表推导式生成列表:

# 普通for循环生成l1
l1 = []
for i in [10,11,12]:
    l1.append(test_recursion(i))

# 列表推导式生成l2
l2 = [test_recursion(i) for i in [10,11,12]]

执行print(l1, l2, sep = "\n")得到输出:

[7,8,9]
[6,7,8]

该差异同样存在于字典推导式和集合推导式中,需要解释该现象的原因。

原因解析

核心差异:推导式的内部实现依赖隐式函数

Python的列表/集合/字典推导式,在编译阶段会被转换为一个隐式的内部函数,执行推导式时会调用这个内部函数,这会额外占用1个调用栈槽;而普通for循环是直接在当前栈帧(比如主程序栈帧)中执行,不会新增额外的栈帧层级。

栈帧占用对比

  • 普通for循环场景:调用栈结构为主程序栈帧 → test_recursion栈帧 → factorial递归栈帧。test_recursion本身占用1个栈槽,当设置sys.setrecursionlimit(n)时,留给factorial递归的栈槽数为n - 2(主程序+test_recursion各占1个)。以n=10为例,factorial最多可递归8层(对应num=7,因为factorial(7)的递归深度是8:7→6→…→0),此时总栈深度刚好达到10,触发错误,返回7。
  • 推导式场景:调用栈结构为主程序栈帧 → 推导式内部函数栈帧 → test_recursion栈帧 → factorial递归栈帧。比普通循环多了一层推导式内部函数的栈槽占用,留给factorial递归的栈槽数为n - 3。以n=10为例,factorial最多可递归7层(对应num=6),总栈深度达到10时触发错误,返回6。

验证方式

可以通过inspect模块查看当前栈帧的深度,在test_recursion函数中加入以下代码:

import inspect
print(len(inspect.stack()))

执行后会发现:普通循环中输出2(主程序+test_recursion),推导式中输出3(主程序+推导式内部函数+test_recursion),直接证明了推导式多占用了一层栈帧。

总结

  • 推导式通过隐式内部函数实现,会额外占用1个调用栈槽;
  • 普通循环无额外栈帧开销,可用递归栈空间比推导式多1;
  • 因此在递归栈受限的场景下,推导式中触发RecursionError的最小num值会比普通循环小1,这就是输出差异的根源。

内容的提问来源于stack exchange,提问作者Rajdeep Sindhu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 18:06:21