为啥 Python 有最大递归深度?
Posted
技术标签:
【中文标题】为啥 Python 有最大递归深度?【英文标题】:Why does Python have a maximum recursion depth?为什么 Python 有最大递归深度? 【发布时间】:2015-01-08 12:25:06 【问题描述】:Python 有最大递归深度,但没有最大迭代深度。为什么递归受限?像迭代一样对待递归,不限制递归调用的次数不是更自然吗?
我只想说这个问题的根源来自尝试实现流(有关流的更多详细信息,请参阅this question)。例如,假设我们要编写一个流来生成自然数:
def stream_accum(s, n): # force the stream to a list of length n
def loop(s, acc):
if len(acc) == n:
return acc
hd, tl = s()
return loop(tl, acc + [hd])
return loop(s, [])
def nats():
def loop(n):
return n, lambda: loop(n+1)
return loop(1)
流的递归定义非常吸引人。但是,我想更好/更 Python 的方法是使用生成器。
【问题讨论】:
“吸引人”的递归解决方案有许多不吸引人的方面。首先,它具有 O(n**2) 行为,因为您不断构建新列表来扩展它们。其次,它过于复杂,因为您可以简单地迭代以产生自然数。这是一个编写 Python 的示例,就好像它是 Scheme 或 Haskell。不同的语言擅长不同的事情。使用迭代。 【参考方案1】:递归需要call stack 上的空间,它的大小是有限的。使用太多递归级别的代码将给出一个称为stack overflow 的错误(也因some obscure website 而闻名)。 Python 似乎将这个(有点武断)限制在 1000 级左右,但是这个 can be increased 是通过设置 sys.setrecursionlimit
来实现的。
迭代使用类似for
-loop 的东西,它是通过增加一些计数器并有条件地将instruction pointer 设置回循环的开头来实现的。这在内存中是恒定的。
【讨论】:
【参考方案2】:这不是 Python 独有的,与每个调用占用 call stack 上的空间以及堆栈大小受到限制有关。
单独的迭代不会消耗堆栈空间,因此不受此限制。
并非每个递归调用都需要消耗堆栈空间。例如,某些语言可以自动将tail recursion 转换为迭代。但是,CPython 选择不这样做 (Does Python optimize tail recursion?)。
您可以通过调用 sys.setrecursionlimit
来增加 Python 调用堆栈的最大深度。
【讨论】:
栈的大小并没有真正的限制。 (除了堆是有限的。)Python 故意选择限制它。【参考方案3】:这里实际上存在一些问题。
首先,正如NPE's answer 很好地解释的那样,Python 并没有消除尾调用,所以很多允许无限递归的函数,比如 Scheme,在 Python 中是有限的。
其次,正如 NPE 所解释的那样,无法消除的调用会占用调用堆栈上的空间。而且,即使在使用 TCE 的语言中,也有很多不能像迭代一样对待的递归函数。 (考虑递归调用自身两次的朴素斐波那契函数。)
但为什么调用堆栈首先是有限资源? Python 堆栈帧至少原则上可以在堆上实现并链接在一起(参见Stackless 以获得该原理的存在证明),并且在 64 位内存空间中,有超过 1000 个堆栈帧的空间. (事实上,几乎任何现代平台上的 C 堆栈都可以容纳超过 1000 个递归 Python 解释器调用。)
部分原因是历史原因:当您进行递归调用时,普通的 Python 解释器使用固定的 C 堆栈递归调用自身,它最初是为 32 位(甚至 24 或 20 位)平台设计的C 堆栈非常小。
但这本可以改变,Python 3.0 将是改变它的完美场所。那么,他们为什么不呢?因为他们做出了有意识的语言设计决定。在 Pythonic 代码中,递归通常很浅(例如,像 os.walk
这样的代码遍历浅树结构);如果您的函数达到接近 1000 的深度,则它更有可能是一个错误而不是故意的。所以,限制仍然存在。当然,这有点循环——如果他们取消了限制(特别是,如果他们取消了尾调用),更深层次的递归将变得更加惯用。但这就是重点——Guido 不想要一种深度递归是惯用的语言。 (大多数 Python 社区都同意。)
【讨论】:
注意:“你可以通过调用sys.setrecursionlimit
来增加Python调用栈的最大深度”以上是关于为啥 Python 有最大递归深度?的主要内容,如果未能解决你的问题,请参考以下文章