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

为何eval()调用占用两倍递归深度?与其他函数的栈差异解析

为什么eval()/exec()比普通函数消耗更多递归栈空间?

现象重现

先看测试代码:

from sys import getrecursionlimit
print(f'sys: maxRecursionDepth = {getrecursionlimit()}')
cnt = 0
def f3(s):
    global cnt
    cnt += 1
    eval(s)

try:
    f3('f3(s)')
except RecursionError:
    print(f'f3() maxRecursionDepth =  {cnt}')

输出结果:

sys: maxRecursionDepth = 1000
f3() maxRecursionDepth =  333

可以看到,递归调用的次数仅为系统递归限制的1/3左右,远低于普通递归函数能达到的接近1000的次数。这说明每次通过eval()触发递归时,额外消耗了2个栈层级。

深层原因

这种差异源于eval()和exec()的底层执行机制:

  • 普通函数调用:当直接调用自定义或内置函数时,Python使用CALL_FUNCTION字节码指令,直接在当前执行流中创建一个新的函数栈帧,整个过程仅增加1个栈帧。
  • eval()/exec()的执行流程:这两个函数处理的是字符串形式的动态代码,执行时需要多两步额外的栈帧开销:
    1. 调用eval()本身会创建一个栈帧(内置函数的调用帧);
    2. eval()内部需要为传入的字符串代码创建一个独立的执行上下文(栈帧)——它要先把字符串解析为AST、编译成字节码,再在这个新的上下文里执行代码。当这段代码触发f3(s)调用时,又会创建一个新的f3栈帧。

也就是说,每一轮递归实际会新增3个栈帧:f3帧 → eval帧 → 动态代码执行帧 → 下一个f3帧,所以系统的1000栈帧限制下,f3的调用次数约为1000/3≈333。

这种额外的栈帧是eval()/exec()动态执行代码的固有开销,和系统栈被其他进程占用无关——这也是你提到的另一个问题答案不适用的原因,那个问题解释的是系统级的栈占用,而这里是Python解释器层面的执行机制导致的差异。

总结

eval()/exec()和普通函数的核心区别在于:它们需要为动态代码创建额外的执行上下文栈帧,而普通函数调用仅需要一个函数栈帧,这直接导致了递归时栈消耗的倍数级差异。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 06:10:30