Python递归运行时错误
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 个回答
异常仍然在被抛出,但在 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
当栈慢慢填满的时候,它会在 try
里面不断调用 f2
,直到达到最大递归深度。
一旦达到这个深度,就会抛出一个 RuntimeError
,这个错误会被 finally
处理。
然后,这个 finally
又会抛出同样的 RuntimeError
,不过这次是给之前的栈,接着又传递到 finally
的调用。
在这个过程中,它又一次超过了最大深度。
当出现 KeyboardInterrupt
时,程序依然会进入 finally
,而不会直接退出。
从技术上讲,它不会永远运行,因为只有一个 finally
。不过,正如评论所说,这样可以允许指数级的调用,几乎接近无穷大。如果递归深度是100,那就会变成 2100 == 1267650600228229401496703205376。
如果每次调用耗时1毫秒,那完成这个过程就需要4650亿年。而这只是深度为100的情况。