Python递归限制与堆栈大小?

时间:2018-05-05 19:13:36

标签: python recursion stack

我理解递归中堆栈上的每个递归调用堆栈是怎么回事;如果超出堆栈限制,则会出现堆栈溢出。 那么,为什么Python的getrecursionlimit()返回一个数字 - 最大深度的递归调用?它不依赖于我在递归函数中的作用吗?或者以某种方式将变量保存在除堆栈之外的其他位置?它是如何工作的?

1 个答案:

答案 0 :(得分:10)

考虑到这一点的简单方法(如果过于简化)是Python堆栈实际上并不是一个包含所有帧连接的巨型数组,而是一个链接的框架列表。 1 但即便如此如果你正在考虑C语言,那就会产生误导。你似乎是:

  

或者以某种方式将变量保存在其他地方,而不是堆栈?

- 在CPython中,局部变量 2 存储在堆分配的帧对象的数组中 - 但这通常不是相关的问题

在C中,变量是类型化的内存位置。当您编写int lst[100];时,它会在堆栈上分配400个字节并将其命名为lst

在Python中,变量只是值的名称(在某些名称空间中)。内存位置(和类型)是值的属性,而不是变量,它们总是位于堆中的某个位置。 3 变量只是对它们的引用。因此,如果您编写lst = [0]*100,那么对于locals数组中的变量(指针),只有8个字节,然后是堆上列表对象的864个字节。 4

RecursionError限制是因为大多数 Python代码深度为1000,可能需要花费很长时间才能在失败之前分配一大堆Python帧无论是MemoryError还是堆栈溢出段错误,所以最好在分配所有内存并烧掉所有CPU之前阻止你。

更重要的是,正如tdelaney在评论中指出的那样,在Python中恢复这些条件非常困难 - 但从RecursionError恢复非常简单;它会将堆栈展开到递归的顶部,让您处于可预测的状态。

但是这个经验法则并不适用于每个程序,只是大部分程序 - 所以如果你知道你的算法可能有几千帧而没有任何问题, Python允许您将限制增加到10000而不是1000。

<子> 1。这是过于简单的,因为(至少在CPython中)解释器 经常实际上在C堆栈上链接调用 - 但它仍然有用于记住新的帧对象(以及框架分配的其他内容)每次在Python中递归时都会分配堆,无论解释器是否递归。 (特别是因为Python被定义为在Python级别从不进行尾调用消除,即使解释器实际上在eval循环中这样做了。)

<子> 2。从技术上讲,在Python中,所有变量都存储在命名空间中,从名称到值的引用。但CPython通过存储指针数组来优化局部变量,然后让编译器将本地引用转换为数组查找而不是映射查找。

<子> 3。当然那是&#34;某个地方&#34;未指定 - Python是垃圾收集的,无论是使用自动引用计数加上CPython中的循环检测器,还是JVM中使用的底层JVM。但在CPython中,还有一个已定义的C API,其中对象是指向结构的C指针 - 您可以使用id函数查看此指针的值。

<子> 4。此外,864字节大多只是一个指向单个不可变0对象的100个指针的列表,与C不同,其中有100个单独的可变int个插槽,它们都具有值0 in它们。