你清楚Python的sys.setrecursionlimit到底改变了什么底层参数吗

访客 源码剖析 1

本文目录导读:

  1. 核心底层参数:Py_RecursionLimit
  2. 它如何控制递归 “深度”?
  3. 不改变但受限于的底层参数:C 栈大小(OS 级)
  4. 一个容易被忽略的点:sys.setrecursionlimit 并没有设置 “虚拟内存” 或 “栈预留空间”
  5. 总结与建议
  6. 补充:如何查看当前递归深度?

sys.setrecursionlimit 主要改变的是 Python 解释器(CPython)内部的递归深度上限,但它并不直接改变 C 语言层面的栈空间大小或操作系统的栈限制,底层具体影响的参数和机制如下:


核心底层参数:Py_RecursionLimit

  • 在 CPython 源码(Python/ceval.cInclude/cpython/ceval.h)中,有一个全局变量 int Py_RecursionLimit
  • sys.setrecursionlimit(n) 本质上就是执行:
    Py_RecursionLimit = n;
  • 这个值默认是 1000(Python 3.5+,早期版本常用 1000)。

它如何控制递归 “深度”?

CPython 在执行 Python 函数调用时,会在 执行栈(call stack) 上增加一层帧(frame),每次进入一个函数,解释器内部都会检查当前递归深度:

if (tstate->recursion_depth > Py_RecursionLimit) {
    PyErr_SetString(PyExc_RecursionError, "maximum recursion depth exceeded");
    // 触发异常
}
  • 递归深度tstate->recursion_depth 记录,它是一个 int,每次函数调用 +1,返回时 -1。
  • setrecursionlimit 改变的正是这个 if 比较的阈值。

不改变但受限于的底层参数:C 栈大小(OS 级)

  • Python 的递归最终会消耗 C 栈空间(因为 Python 函数调用最终由 C 函数 PyEval_EvalFrameDefault 等递归驱动)。
  • 如果设置过大的 recursionlimit(如 100000),虽然 Python 层面不报错,但C 栈会溢出,导致 Segmentation Fault(段错误)或 Stack Overflow(栈溢出),而不是 Python 异常。
  • 这个限制由 操作系统线程栈大小ulimit -s 在 Linux/macOS)或 可执行文件链接时的栈大小 决定。

一个容易被忽略的点:sys.setrecursionlimit 并没有设置 “虚拟内存” 或 “栈预留空间”

  • 不是修改 setrlimit(RLIMIT_STACK)mmap 的栈增长参数。
  • 只是一个纯软件计数器阈值,完全在 Python 层面,不涉及任何操作系统系统调用

总结与建议

参数层次 是否被setrecursionlimit改变 典型默认值
Python 层面 Py_RecursionLimit 1000
解释器 C 层面 C 函数调用栈 取决于 OS 栈大小
操作系统层面 线程栈大小(RLIMIT_STACK) 8 MB(Linux 默认)

所以

  • 如果你把 recursionlimit 设得太大(100000),而你的递归函数每次调用消耗大量局部变量或深层嵌套,则真实危险是 C 栈溢出 → Segmentation Fault
  • 唯一安全的方式是同时增大操作系统的线程栈大小(例如通过 threading.stack_size()ulimit -s),但这与 setrecursionlimit 无关。

补充:如何查看当前递归深度?

import sys
# 查看当前递归深度(需要小心,调用该函数自身也会增加一层深度)
print(sys.getrecursionlimit())   # 返回当前递归限制

如果你需要底层源码细节,可以参考 CPython 的 Python/ceval.c 中的 _PyEval_EvalFrameDefault 函数里:

if (tstate->recursion_depth++ > Py_RecursionLimit) {
    // raise RecursionError
}

标签: 递归深度限制

抱歉,评论功能暂时关闭!