我理解在递归中每次递归调用都会堆叠在栈上;如果超过了栈限制,就会出现堆栈溢出。
那么Python的sys.getrecursionlimit()
为什么会返回一个数字,即递归调用的最大深度呢?
它不是取决于我在递归函数中做了什么吗?还是说它会在其他地方保存变量,而不是栈上?它是如何工作的呢?
lst = [0]*100
,则变量(指针)在本地数组中只占用8字节,而堆中的列表对象需要864字节。 RecursionError
限制存在是因为大多数深度达到1000的Python代码可能只需要分配大量的Python帧就会花费很长时间,然后在MemoryError
或栈溢出段错误失败,因此最好在分配所有那些内存和CPU之前停止它。更重要的是,正如tdelaney在评论中指出的那样,从其中任何一种情况恢复在Python中都非常困难,但从RecursionError
中恢复相当简单;它会将堆栈解开至递归的顶部,并将您留在可预测的状态中。但是这个经验法则并不适用于所有程序,仅适用于大多数程序 - 因此,如果您知道自己的算法可能会深入几千个帧而没有任何问题,Python允许您将限制增加到10000而不是1000。
1. 这个规则过于简单了,因为(至少在CPython中)解释器实际上经常在C栈上链接调用,但仍然有用的是要记住每次在Python中进行递归时都会分配一个新的 frame 对象(以及 frame 分配的其他内容),无论解释器是否正在递归。 (特别是因为 Python 被定义为在 Python 级别永远不会执行尾递归消除,即使解释器在 eval 循环中实际上进行了这样的操作。)
2. 严格地说,在 Python 中,所有变量都存储在命名空间中,这是从名称到值引用的映射。但是,CPython 通过存储指针数组来优化局部变量,然后让编译器将本地引用转换为数组查找而不是映射查找。
3. 当然,“某处”是未指定的 - Python 是垃圾收集的,无论使用 CPYthon 中的自动引用计数加循环检测,还是 Jython 中底层 JVM 使用的任何方式。但在 CPython 中,也有一个定义的 C API,其中对象是指向结构体的 C 指针 - 您可以使用 id
函数查看此指针的值。
4. 此外,这个 864 字节主要只是一个指向单个不可变的 0
对象的 100 个指针的列表,不像在 C 中,有 100 个分离的可变的 int
插槽,它们都具有值 0
。
RecursionError
中恢复过来,但是你很少能够从将递归深度增加到荒谬程度导致的MemoryError
中恢复过来... - abarnert