recursionerror是cpython为防栈溢出主动限制的保护机制,默认1000层基于系统线程栈大小估算,调高有静默崩溃风险,推荐改用迭代或pypy等替代方案。

RecursionError: maximum recursion depth exceeded 不是 Python 写错了,而是 CPython 主动拦停——它怕你把底层 C 栈吃光,直接崩掉整个进程。
为什么默认限制是 1000?
这个数字跟物理内存无关,只跟操作系统给每个线程分配的 C 栈大小有关(Linux 默认约 8MB,Windows 更小)。每层递归至少占 1–2KB 栈帧,1000 层 ≈ 1–2MB,留了足够余量防爆栈。
设太高:还没报 RecursionError 就 Segmentation fault 或被 OS 杀掉(Killed: 9);
设太低:正常解析 500 层深的 JSON 或 AST 就直接误伤。
-
sys.getrecursionlimit()返回当前值(通常就是 1000) - 该限制是解释器启动时根据系统栈估算的保守值,不是硬编码
- 多线程下,子线程栈更小,
sys.setrecursionlimit()对它无效
手动调高 sys.setrecursionlimit() 的危险点
调高只是改计数阈值,不增加真实栈空间。以下情况极易出事:
- 在
__str__或__repr__里打印对象自身,触发隐式递归链 - 用装饰器或日志钩子间接再进入同一函数
- 直接设成
sys.setrecursionlimit(100000)去硬扛 DFS —— 大概率静默崩溃 - 线上服务中无差别调高,其他模块可能依赖默认行为
真要改,必须:
- 先确认递归逻辑正确、深度可预估(比如固定结构的配置文件解析)
- 用
try/except RecursionError包裹调用,不能只信设置值 - 设完立刻用
sys.getrecursionlimit()验证是否生效 - 设完记得恢复原值:
sys.setrecursionlimit(old_limit)
比调 limit 更靠谱的解法:转成迭代
绝大多数递归都能用显式栈 + 循环替代,既绕过限制,又可控内存占用。例如 DFS:
def dfs_iterative(graph, start):
stack = [start]
visited = set()
while stack:
node = stack.pop()
if node not in visited:
visited.add(node)
stack.extend(graph[node] - visited)
return visited
- 递归版 DFS 在树深 2000 时大概率崩;迭代版栈里最多存树高个节点,内存开销明确
- 避免用
list.pop(0)模拟队列,改用collections.deque - CPython 不支持尾递归优化,所谓“尾递归写法”照样压栈,没用
哪些场景真需要深递归?务实选择
如果业务确实绕不开(比如符号计算、超深嵌套配置合并),优先考虑:
- 换 PyPy:它的栈管理更灵活,对深递归容忍度更高
- 用生成器延迟求值,把一次性展开变成按需取值
- 拆分任务:把单次深递归拆成多个浅层调用 + 中间状态持久化
别迷信 sys.setrecursionlimit(),它解决不了栈空间本质限制。真正容易被忽略的是:递归崩掉前,往往已经悄悄吃掉了几 MB 栈内存——而你根本看不到监控指标。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











