Python 的递归深度限制是什么?为什么 Python 不做尾递归优化?
简化版
**Python 默认限制递归深度约 1000 层(sys.getrecursionlimit()),超过就抛 RecursionError: maximum recursion depth exceeded,防止无限递归把「调用栈」撑爆导致解释器崩溃。**关键点:① 为什么有限制——每次函数调用会在调用栈上压一个「栈帧」(保存局部变量、返回地址),递归太深会耗尽栈空间;Python 用一个「软限制」(默认 1000)在真正崩溃前主动抛异常(比 C 栈溢出崩溃更友好);② 可以调整——sys.setrecursionlimit(n) 能改,但调太大有真的栈溢出崩溃风险(Python 层限制之外还有 C 栈的物理限制);③ Python 不做尾递归优化(TCO)——即使写成尾递归形式,Python 也不会把它优化成循环,Guido 明确拒绝(理由:会破坏「完整的调用栈回溯」、递归不是 Python 推荐的循环方式)。结论:Python 里深递归应改写成「迭代(循环)」或用「显式栈」,别指望尾递归优化。例子:深度遍历、阶乘、斐波那契用循环或记忆化更好。核心记忆:递归深度默认约 1000(可 setrecursionlimit 调但有栈溢出风险),Python 不做尾递归优化(Guido 拒绝),深递归改写成迭代/显式栈。
详细版
递归深度限制:
| 方面 | 说明 |
|---|---|
| 默认限制 | 约 1000(sys.getrecursionlimit()) |
| 超限 | 抛 RecursionError |
| 调整 | sys.setrecursionlimit(n)(有栈溢出风险) |
| 尾递归优化 | Python 不做(Guido 拒绝) |
| 深递归的解法 | 改写成迭代 / 显式栈 |
import sys
# 查看和设置递归限制
print(sys.getrecursionlimit()) # 1000(默认)
# 无限递归会触发 RecursionError(而非崩溃)
def bad(n):
return bad(n + 1)
# bad(0) # RecursionError: maximum recursion depth exceeded
# 深递归超限例子
def depth(n):
if n == 0: return 0
return depth(n - 1) + 1
# depth(2000) # RecursionError(超过 1000)
# 调整限制(谨慎!可能真的栈溢出崩溃)
sys.setrecursionlimit(3000)
print(depth(2000)) # 现在能跑(但风险自负)
sys.setrecursionlimit(1000) # 改回
# Python 不优化尾递归——写成尾递归也一样会超限
def fact_tail(n, acc=1):
if n == 0: return acc
return fact_tail(n - 1, acc * n) # 尾调用,但 Python 不优化
# fact_tail(2000) # 仍然 RecursionError!
# 正确做法:改写成迭代(循环)
def fact_iter(n):
acc = 1
for i in range(1, n + 1):
acc *= i
return acc
print(fact_iter(2000)) # ✓ 无深度限制,能算超大阶乘
# 深度优先遍历:递归 → 显式栈
def dfs_iter(root):
stack = [root]
while stack:
node = stack.pop()
# 处理 node
stack.extend(node.children) # 用列表当栈,不受递归限制
⚠️ 两个核心认知:① Python 限制递归深度(默认 ~1000)是一种「保护」——每层递归在调用栈上占一个栈帧,太深会耗尽内存/栈空间,Python 在真正崩溃前主动抛
RecursionError(比 C 那种直接段错误崩溃友好);② Python 故意不做「尾递归优化」,所以在 Python 里递归不是深层循环的好工具,深递归要改成迭代。很多人从函数式语言(Scheme、Haskell)带来「尾递归 = 循环」的直觉,在这些语言里尾递归会被优化成不增长栈的循环;但 Guido 明确拒绝在 Python 里加 TCO,理由很实际:① 会破坏「完整的栈回溯(traceback)」——调试时看不到完整调用链;② Python 的哲学是「显式循环优于递归」,不想鼓励用递归模拟循环;③ 实现复杂、收益不大。所以在 Python 里:能用循环就用循环(阶乘、求和、遍历);天然递归的问题(树/图遍历、分治)如果深度可能很大,改成「显式栈 + while 循环」(把递归的隐式调用栈换成自己管理的列表);sys.setrecursionlimit可以调高但要小心——它只是抬高 Python 的软限制,底层 C 栈仍有物理上限,调太高会真的段错误崩溃。
完整版教学
一、递归与调用栈
先理解递归为什么会有深度问题:
递归:函数调用自己
def f(n):
if n == 0: return 0 # 基线条件(base case)
return f(n-1) + 1 # 递归调用
每次函数调用 → 压一个"栈帧"到调用栈:
栈帧保存:局部变量、参数、返回地址(调用完回哪)
f(3) 调 f(2) 调 f(1) 调 f(0)
→ 栈上同时有 4 个栈帧(f0,f1,f2,f3 都还没返回)
调用栈(call stack):
[f(3)] ← 最先调用
[f(2)]
[f(1)]
[f(0)] ← 最后调用(栈顶)
→ f(0) 返回后逐层弹出、回填结果
深度问题:
递归越深 → 栈帧越多 → 占用栈空间越大
无限递归 / 太深 → 栈空间耗尽 → 崩溃
栈空间有限:
程序的调用栈大小有限(几 MB)
每个栈帧占若干字节
→ 太多栈帧会溢出(stack overflow)
对比迭代(循环):
循环不压新栈帧,只有一个栈帧、复用变量
→ 无深度限制(只受数据量限制)
所以递归每层压一个栈帧,栈空间有限,太深会溢出;迭代不压栈帧无深度限制
递归:函数调用自己(要有基线条件 base case 终止)。每次函数调用压一个「栈帧」到调用栈:栈帧保存局部变量、参数、返回地址。f(3)→f(2)→f(1)→f(0) 时栈上同时有 4 个栈帧(都没返回)。调用栈:后调用的在栈顶、f(0) 返回后逐层弹出回填结果。深度问题:递归越深栈帧越多、占用栈空间越大、无限/太深会耗尽栈空间崩溃。栈空间有限:程序调用栈几 MB、每个栈帧占若干字节、太多会溢出(stack overflow)。对比迭代(循环):循环不压新栈帧、只有一个栈帧复用变量、无深度限制(只受数据量限制)。理解「递归每层压一个栈帧(存局部变量/参数/返回地址),栈空间有限(几 MB),太深会栈溢出;迭代不压栈帧、只一个栈帧复用、无深度限制」,就理解了递归的深度问题。
二、Python 的递归深度限制
理解 Python 的软限制机制:
Python 的递归深度限制:
sys.getrecursionlimit() → 1000(默认)
→ 递归深度超过约 1000 层,抛 RecursionError
为什么设"软限制"(Python 层面主动检查):
① C 栈溢出会导致解释器"段错误崩溃"(无法恢复、无堆栈信息)
② Python 在每次函数调用时检查深度,超限就抛 Python 异常
→ 可被 try/except 捕获、有清晰的错误信息
→ 比直接崩溃友好得多
RecursionError 信息:
RecursionError: maximum recursion depth exceeded
→ 告诉你递归太深(可能是忘了基线条件的无限递归)
限制不完全等于 1000 层用户函数:
限制是"C 栈的帧数",包括解释器内部调用
→ 实际用户递归可能到不了 1000(内部调用也占)
调整限制:
sys.setrecursionlimit(n)
→ 抬高软限制(但 C 栈物理上限还在)
→ 调太高:软限制过了、C 栈爆了 → 真的段错误崩溃
→ 一般不建议大幅调高(说明该改迭代了)
查看当前深度:
len(inspect.stack()) # 当前调用栈深度
所以 Python 软限制默认 1000(超抛 RecursionError),比 C 栈崩溃友好,可调但有风险
Python 的递归深度限制:sys.getrecursionlimit() → 1000(默认),超过约 1000 层抛 RecursionError。为什么设「软限制」(Python 层面主动检查):① C 栈溢出会导致解释器段错误崩溃(无法恢复、无堆栈信息)、② Python 每次函数调用检查深度、超限抛 Python 异常(可被 try/except 捕获、有清晰错误信息、比崩溃友好)。RecursionError 信息:maximum recursion depth exceeded(可能是忘了基线条件的无限递归)。限制不完全等于 1000 层用户函数:限制是「C 栈的帧数」(包括解释器内部调用、实际用户递归可能到不了 1000)。调整限制:sys.setrecursionlimit(n) 抬高软限制(但 C 栈物理上限还在、调太高会真的段错误崩溃、一般不建议大幅调高——说明该改迭代了)。理解「Python 软限制默认 1000(超抛 RecursionError),软限制在真正崩溃前主动抛异常比 C 栈段错误友好;限制含内部调用;setrecursionlimit 可调但 C 栈物理上限还在、调太高真崩溃」,就掌握了递归深度限制。
三、Python 不做尾递归优化
理解为什么 Python 没有 TCO:
尾递归(tail recursion):
递归调用是函数的"最后一个动作"(返回值就是递归调用的结果)
def fact(n, acc=1):
if n == 0: return acc
return fact(n-1, acc*n) # 尾调用(最后就是递归,无额外计算)
尾递归优化(TCO, Tail Call Optimization):
有些语言(Scheme、Haskell、部分 JS 引擎)会优化:
尾递归时"复用当前栈帧"(不压新帧)
→ 尾递归变成"循环",不增长栈、无深度限制
Python 不做 TCO:
即使写成尾递归,Python 也照常压栈帧
→ fact(2000) 仍然 RecursionError(不会被优化)
Guido 明确拒绝 TCO(理由):
① 破坏栈回溯(traceback)——优化后调试看不到完整调用链
Python 重视清晰的错误堆栈
② 哲学:显式循环优于递归——不想鼓励用递归模拟循环
"Python 不是函数式语言,别把递归当循环用"
③ 实现代价 vs 收益——加 TCO 复杂、收益有限
④ 隐式优化违反"显式优于隐式"
结论:
在 Python 里"尾递归 = 循环"的直觉不成立
→ 尾递归和普通递归一样受深度限制
→ 深递归必须手动改成迭代
对比其他语言:
Scheme: 尾递归 = 循环(语言规范要求 TCO)
Python: 尾递归 = 普通递归(无优化)
→ 从函数式语言转来要调整思维
所以 Python 不做尾递归优化(Guido 拒绝:破坏栈回溯+显式循环优于递归),尾递归也受限
尾递归(tail recursion):递归调用是函数的「最后一个动作」(返回值就是递归调用的结果,return fact(n-1, acc*n) 无额外计算)。尾递归优化(TCO):有些语言(Scheme、Haskell、部分 JS 引擎)会优化——尾递归时复用当前栈帧(不压新帧),尾递归变成循环(不增长栈、无深度限制)。Python 不做 TCO:即使写成尾递归也照常压栈帧(fact(2000) 仍 RecursionError)。Guido 明确拒绝 TCO(理由):① 破坏栈回溯(优化后调试看不到完整调用链)、② 哲学(显式循环优于递归、Python 不是函数式语言)、③ 实现代价 vs 收益、④ 隐式优化违反「显式优于隐式」。结论:Python 里「尾递归 = 循环」的直觉不成立、深递归必须手动改成迭代。理解「Python 不做尾递归优化(TCO);尾递归调用是最后动作、有的语言会复用栈帧变循环、Python 不会;Guido 拒绝:破坏栈回溯+显式循环优于递归+代价大;尾递归也受深度限制、必须手动改迭代」,就掌握了为什么 Python 没有 TCO。
四、深递归的正确改写
理解怎么把深递归改成迭代:
深递归的改写方案:
方案1:线性递归 → 循环(最简单)
# 递归阶乘(受深度限制)
def fact(n):
if n == 0: return 1
return n * fact(n-1)
# 改成循环(无深度限制)
def fact(n):
acc = 1
for i in range(1, n+1): acc *= i
return acc
方案2:树/图递归 → 显式栈 + 循环
# 递归 DFS(深树会超限)
def dfs(node):
visit(node)
for c in node.children: dfs(c)
# 改成显式栈
def dfs(root):
stack = [root]
while stack:
node = stack.pop()
visit(node)
stack.extend(reversed(node.children)) # 用列表当栈
→ 把"递归的隐式调用栈"换成"自己管理的列表"
→ 不受递归深度限制(只受内存限制)
方案3:记忆化(减少递归次数,但不减深度)
# 斐波那契:记忆化避免重复计算(但深度仍在)
from functools import lru_cache
@lru_cache
def fib(n):
if n < 2: return n
return fib(n-1) + fib(n-2)
→ 记忆化解决"指数爆炸",但深度问题要靠迭代
方案4:确实需要深递归 → 调高限制(谨慎)
sys.setrecursionlimit(10000) # 风险自负(可能真崩溃)
选择:
线性递归 → 循环
树/图/分治 → 显式栈(栈/队列)+ 循环
重复子问题 → 记忆化 + 迭代
实在要深递归 → 调限制(但优先改迭代)
所以改写:线性递归→循环、树图→显式栈+循环、重复→记忆化;别轻易调限制
深递归的改写方案:方案1:线性递归 → 循环(最简单)(阶乘、求和改成 for 循环、无深度限制);方案2:树/图递归 → 显式栈 + 循环(递归 DFS 改成 stack = [root]; while stack: node = stack.pop(); stack.extend(...),把递归的隐式调用栈换成自己管理的列表、不受递归深度限制);方案3:记忆化(减少递归次数,但不减深度)(斐波那契用 @lru_cache 避免重复计算、但深度问题要靠迭代);方案4:确实需要深递归 → 调高限制(谨慎)(sys.setrecursionlimit(10000) 风险自负)。选择:线性递归用循环、树/图/分治用显式栈、重复子问题用记忆化+迭代、实在要深递归调限制(但优先改迭代)。理解「改写:①线性递归→循环②树图递归→显式栈+while(把隐式调用栈换成自己的列表)③记忆化 lru_cache 减少次数但不减深度④调 setrecursionlimit(谨慎);优先改迭代别轻易调限制」,就掌握了深递归改写。
五、递归的适用与陷阱
理解递归什么时候合适、有什么坑:
递归适合的场景(Python 里):
① 天然递归结构,且深度可控(< 1000):
树遍历(不太深)、分治(归并/快排)、回溯
② 表达清晰胜过性能:
递归写法更接近问题本质(如汉诺塔)
→ 深度可控时递归代码更简洁易懂
递归的陷阱:
① 忘了基线条件 → 无限递归 → RecursionError
def f(n): return f(n-1) # 永远不停
② 深度可能很大 → 超限(图算法、深树、大 n)
③ 重复计算 → 指数爆炸(朴素斐波那契 O(2^n))
→ 记忆化解决
④ 每层栈帧开销 → 递归比迭代慢、占内存
Python 递归慢的原因:
函数调用开销大(压栈、传参、Python 解释开销)
→ 同样逻辑,迭代通常比递归快
性能对比(概念):
fact 递归 vs 迭代:迭代快(无函数调用开销)
DFS 递归 vs 显式栈:显式栈无深度限制、通常也更快
实践建议:
① 深度可控 + 天然递归 → 递归(简洁)
② 深度可能大 → 迭代/显式栈
③ 重复子问题 → 记忆化
④ 性能敏感的深循环 → 迭代
所以递归适合深度可控的天然递归结构;陷阱:无限递归/超限/重复计算/慢
递归适合的场景(Python 里):① 天然递归结构且深度可控(< 1000)(树遍历、分治归并/快排、回溯)、② 表达清晰胜过性能(递归更接近问题本质、如汉诺塔)。递归的陷阱:① 忘了基线条件 → 无限递归 → RecursionError、② 深度可能很大 → 超限、③ 重复计算 → 指数爆炸(朴素斐波那契 O(2^n)、记忆化解决)、④ 每层栈帧开销 → 递归比迭代慢/占内存。Python 递归慢的原因:函数调用开销大(压栈、传参、解释开销)、同样逻辑迭代通常更快。实践建议:深度可控+天然递归用递归、深度可能大用迭代/显式栈、重复子问题用记忆化、性能敏感用迭代。理解「递归适合深度可控的天然递归结构(树/分治/回溯);陷阱:忘基线条件→无限递归、深度大→超限、重复计算→指数爆炸(记忆化解)、栈帧开销→慢;深度可控用递归、深度大用迭代」,就掌握了递归的适用与陷阱。
六、总结与实践
总结递归限制:
核心:
Python 默认递归深度约 1000(sys.getrecursionlimit)
超过抛 RecursionError(软限制,比 C 栈崩溃友好)
Python 不做尾递归优化(Guido 拒绝)
为什么有限制:
每层递归压一个栈帧,栈空间有限,太深会溢出
软限制在真正崩溃前主动抛异常
为什么不做 TCO:
破坏栈回溯 + 显式循环优于递归(Python 哲学)
深递归的解法:
线性递归 → 循环
树/图/分治 → 显式栈 + while
重复子问题 → 记忆化(lru_cache)+ 迭代
实在要深递归 → setrecursionlimit(谨慎,有真崩溃风险)
调整限制的风险:
setrecursionlimit 只抬软限制,C 栈物理上限还在
→ 调太高会真的段错误崩溃
实践建议:
① 深度可控 + 天然递归 → 递归(简洁)
② 深度可能大 → 迭代/显式栈(无限制、更快)
③ 别指望尾递归优化
核心总结:
递归深度默认 ~1000(可调有风险)
Python 不做尾递归优化
深递归改迭代/显式栈
所以递归深度默认 1000、不做尾递归优化,深递归改迭代/显式栈
核心:Python 默认递归深度约 1000(sys.getrecursionlimit)、超过抛 RecursionError(软限制、比 C 栈崩溃友好)、不做尾递归优化。为什么有限制:每层递归压栈帧、栈空间有限、软限制在崩溃前主动抛异常。为什么不做 TCO:破坏栈回溯 + 显式循环优于递归。深递归的解法:线性递归→循环、树/图/分治→显式栈+while、重复子问题→记忆化+迭代、实在要深递归→setrecursionlimit(谨慎)。调整限制的风险:只抬软限制、C 栈物理上限还在、调太高真崩溃。理解「递归深度默认 1000(超抛 RecursionError、软限制友好)、不做尾递归优化(Guido 拒绝);深递归改迭代/显式栈/记忆化;setrecursionlimit 调太高真崩溃」,就掌握了总结与实践。
记忆钩子:「Python 默认限制递归深度约 1000(sys.getrecursionlimit()),超过抛 RecursionError:maximum recursion depth exceeded;★为什么有限制:每次函数调用在调用栈压一个『栈帧』(存局部变量/参数/返回地址),递归太深耗尽栈空间,Python 用『软限制』在真正崩溃前主动抛 Python 异常(可 try/except、有清晰堆栈,比 C 栈段错误崩溃友好得多);★Python 故意不做『尾递归优化(TCO)』——即使写成尾递归(return f(n-1,acc*n)递归是最后动作),Python 也照常压栈帧、仍会超限,Guido 明确拒绝,理由:①破坏完整栈回溯 traceback(调试看不到调用链)②Python 哲学『显式循环优于递归』不鼓励用递归模拟循环③实现复杂收益小;所以从 Scheme/Haskell 带来的『尾递归=循环』直觉在 Python 不成立;★深递归的正确解法:线性递归改成 for 循环、树/图递归改成『显式栈+while』(stack=[root];while stack:node=stack.pop();stack.extend(children),把递归的隐式调用栈换成自己管理的列表、不受深度限制)、重复子问题用记忆化 lru_cache;sys.setrecursionlimit(n)能调高但只抬软限制、C 栈物理上限还在、调太高会真的段错误崩溃,一般说明该改迭代了」。
七、常见误区与追问
- 误区:Python 的递归深度限制可以随便调大来支持深递归。 不建议——
sys.setrecursionlimit(n)只是抬高 Python 层的「软限制」,但底层 C 调用栈还有物理上限(栈空间是操作系统分配的、几 MB),如果调得太高、软限制过了但 C 栈实际耗尽,会导致解释器「段错误崩溃」(segfault、无法 try/except、无堆栈信息),比 RecursionError 糟糕得多;需要处理深度可能很大的递归时,正确做法是改成迭代或显式栈,而不是盲目调高限制。 - 误区:写成尾递归 Python 就会优化成循环、不受深度限制。 不会——Python 故意不做尾递归优化(TCO),即使递归调用是函数的最后一个动作,Python 照样为每次调用压一个新栈帧,所以尾递归和普通递归一样受深度限制、
fact_tail(2000)照样 RecursionError;从 Scheme、Haskell 等会做 TCO 的语言转来的人常有这个误解;在 Python 里深递归必须手动改成循环或显式栈。 - 误区:RecursionError 一定是代码有 bug(无限递归)。 不一定——RecursionError 有两种情况:① 真的无限递归(忘了基线条件、或基线条件永远不满足),这是 bug;② 递归本身正确、但深度确实超过了限制(比如遍历一棵很深的树、处理很长的链表、大 n 的递归),这不是逻辑 bug 而是「递归深度超限」;第一种要修基线条件,第二种要改成迭代/显式栈(或谨慎调高限制);看到 RecursionError 先判断是哪种。
- 误区:递归和迭代性能一样,只是写法不同。 在 Python 里递归通常更慢、更耗内存——每次递归调用都有函数调用开销(压栈帧、传参、Python 解释器的调用机制),而迭代只用一个栈帧、复用变量;同样的逻辑(如阶乘、求和),迭代版本一般比递归快、且不占额外栈空间、无深度限制;所以性能敏感或深度可能大的场景优先用迭代,递归主要用在「深度可控且递归写法更清晰」的地方(树遍历、分治、回溯)。
- 追问:Python 为什么要限制递归深度,这个限制是怎么起作用的? 因为每次函数调用都会在「调用栈」上压入一个栈帧(保存该次调用的局部变量、参数、返回地址等),栈帧会一直保留到该次调用返回;递归越深、同时存在的栈帧越多、占用的栈空间越大,如果无限递归或递归极深就会耗尽栈空间导致崩溃;Python 设置了一个默认约 1000 的「软限制」,在每次函数调用时检查当前递归深度,一旦超过就主动抛出
RecursionError(一个正常的 Python 异常,可以被捕获、有清晰的堆栈信息)——这样把「C 栈溢出导致的段错误崩溃」(无法恢复、没有有用信息)转化成了「可控的 Python 异常」,既防止了崩溃又给出了友好提示;这个限制包括解释器内部的调用帧,所以实际能达到的用户递归层数可能略少于 1000。 - 追问:Python 为什么不实现尾递归优化,别的语言(如 Scheme)为什么做? Scheme、Haskell 等函数式语言把递归作为主要的循环手段、语言规范要求做尾递归优化(尾调用时复用当前栈帧、不增长栈),所以在它们里「尾递归 = 循环」、可以无限深;Python 的作者 Guido 明确拒绝加 TCO,主要理由有:① 尾递归优化会「压平」调用栈,破坏完整的堆栈回溯(traceback),而 Python 非常重视调试时能看到完整清晰的调用链;② Python 的设计哲学是「显式循环优于递归」,不希望鼓励开发者用递归去模拟循环(那样不 Pythonic);③ 隐式的优化违反「显式优于隐式」的原则,而且实现复杂、收益有限;所以 Python 定位不是函数式语言、递归只是众多工具之一,深循环该用 for/while。
- 追问:遇到深度可能超限的递归(比如遍历很深的树),应该怎么改写? 把「递归的隐式调用栈」改成「自己管理的显式栈 + 循环」:递归 DFS
def dfs(node): visit(node); for c in node.children: dfs(c)可以改成def dfs(root): stack = [root]; while stack: node = stack.pop(); visit(node); stack.extend(reversed(node.children))——用一个列表当栈、pop 取出节点处理、把子节点压回栈,直到栈空;这样递归深度就变成了列表长度、只受内存限制而非递归深度限制;BFS 则用队列(collections.deque)代替栈;线性递归(阶乘、求和、链表遍历)直接改成 for/while 循环;有重复子问题的(如斐波那契)先用记忆化(@lru_cache)减少调用次数、深度大时再配合迭代;总之核心是「把递归换成迭代 + 自己维护的数据结构」,避开 Python 的递归深度限制。
八、加强记忆
Python 默认限制递归深度约 1000(sys.getrecursionlimit()),超过抛 RecursionError: maximum recursion depth exceeded。为什么有限制:每次函数调用在调用栈压一个「栈帧」(存局部变量/参数/返回地址),递归太深耗尽栈空间;Python 用「软限制」在真正崩溃前主动抛 Python 异常(可 try/except、有清晰堆栈,比 C 栈段错误崩溃友好得多)。Python 故意不做「尾递归优化(TCO)」——即使写成尾递归(return f(n-1, acc*n)),Python 也照常压栈帧、仍会超限;Guido 明确拒绝,理由:① 破坏完整栈回溯(调试看不到调用链)、② Python 哲学「显式循环优于递归」(不鼓励用递归模拟循环)、③ 实现复杂收益小;所以从 Scheme/Haskell 带来的「尾递归 = 循环」直觉在 Python 不成立。深递归的正确解法:线性递归改成 for 循环、树/图递归改成「显式栈 + while」(stack = [root]; while stack: node = stack.pop(); stack.extend(children),把递归的隐式调用栈换成自己管理的列表、不受深度限制)、重复子问题用记忆化 lru_cache;sys.setrecursionlimit(n) 能调高但只抬软限制、C 栈物理上限还在、调太高会真的段错误崩溃(一般说明该改迭代了)。一句话「递归深度默认约 1000(超抛 RecursionError、软限制比崩溃友好);Python 不做尾递归优化(Guido 拒绝:破坏栈回溯+显式循环优于递归),尾递归也受限;深递归改成迭代/显式栈+while/记忆化,别轻易调 setrecursionlimit(调太高真崩溃)」。