Python递归运行时错误

8 投票
2 回答
2056 浏览
提问于 2025-04-18 02:05
def f1():
    f1()

我们都知道,在Python中调用这个函数会出现 RuntimeError: maximum recursion depth exceeded 的错误。

我写了一个稍微修改过的版本:

def f2():
    try:
        f2()  #This line throws an error
    finally: #except works too
        f2()  #This line does not throw an error!

第二个函数会一直运行下去,根本不会抛出 RuntimeError。更有意思的是,我甚至无法通过按 CtrlC 的组合键来停止它。

我不明白为什么调用 f2() 不会抛出 RuntimeError。你能解释一下吗?

2 个回答

4

异常仍然在被抛出,但在 Python 能显示这个异常之前,你又调用了 f2() 一次。

所以每次异常被触发时,你都偷偷又调用了一次。这个递归调用是被允许的(因为我们还在限制之下),但当我们超过了限制,异常又被触发,finally 处理器又偷偷调用了一次,几乎是无限循环。

CTRL-C 也无法结束程序,原因是一样的;异常被抛出(KeyboardInterrupt),但同样 finally: 处理器又把你送回了递归中。

你现在以如此快的速度下落,已经进入了解释器的轨道。

这一切最终会结束,但 finally 处理器会在堆栈完全释放之前,增加一个指数级增长的额外调用次数:

>>> import sys
>>> def f2(depth=0, final=0):
...     try:
...         print depth
...         f2(depth + 1, final)
...     finally:
...         print 'finally:', final
...         f2(depth, final + 1)
... 
>>> sys.setrecursionlimit(5)
>>> f2()
0
1
2
3
finally: 0
finally: 0
2
finally: 1
finally: 0
1
2
finally: 1
finally: 1
1
finally: 2
finally: 0
0
1
2
finally: 1
finally: 1
1
finally: 2
finally: 1
0
1
finally: 2
finally: 2
0
finally: 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in f2
  File "<stdin>", line 7, in f2
  File "<stdin>", line 7, in f2
  File "<stdin>", line 7, in f2
RuntimeError: maximum recursion depth exceeded
4

当栈慢慢填满的时候,它会在 try 里面不断调用 f2,直到达到最大递归深度。

一旦达到这个深度,就会抛出一个 RuntimeError,这个错误会被 finally 处理。

然后,这个 finally 又会抛出同样的 RuntimeError,不过这次是给之前的栈,接着又传递到 finally 的调用。

在这个过程中,它又一次超过了最大深度。

当出现 KeyboardInterrupt 时,程序依然会进入 finally,而不会直接退出。

从技术上讲,它不会永远运行,因为只有一个 finally。不过,正如评论所说,这样可以允许指数级的调用,几乎接近无穷大。如果递归深度是100,那就会变成 2100 == 1267650600228229401496703205376。

如果每次调用耗时1毫秒,那完成这个过程就需要4650亿年。而这只是深度为100的情况。

撰写回答