In CPython every function gets its own stack, it's called a frame in CPython and it's an implementation-specific detail(very old one) and other implementation of Python like IronPython1 and Jython doesn't have this functionality or implement it differently.
为了澄清当我们说堆栈时涉及多个堆栈:
- Python堆栈:框架对象的堆栈
- Function values stack: The values in each frame object are stored in this stack to be operated on within the scope of this frame2
- C 堆栈:用于 C 函数调用
当一个函数被调用时,首先创建一个新的框架对象并将其放置在Python stack
。该框架对象包含函数的代码对象、函数可以访问的全局变量以及存储在框架对象中的函数中定义的局部变量。
您可以获取当前帧Python堆栈 https://docs.python.org/3/library/inspect.html#inspect.stack and 当前帧 https://docs.python.org/3/library/inspect.html#inspect.currentframe使用检查模块中提供的实用程序。
问题是它是一个Python对象,它有自己的类型PyFrame_Type https://github.com/python/cpython/blob/456e27ac0ac6bc1cfd6da0191bd7802d8667457b/Objects/frameobject.c#L747,它获取引用计数(获取所有标头PyVarObject https://github.com/python/cpython/blob/456e27ac0ac6bc1cfd6da0191bd7802d8667457b/Include/object.h#L115)并消耗一些内存,如果我们有一系列函数调用,每次我们都会在整个堆的内存中创建这些帧对象。
在 Python 3.11 中,框架对象将被没有对象头的结构数组替换。框架对象仍然可用,但是仅当我们要求时 https://github.com/python/cpython/pull/27077 using inspect.currentframe()
or sys._get_frame()
.
2 Function values stack
我们可以通过访问来检查函数的堆栈大小co_stacksize
函数代码对象的属性,该值在编译时确定:
>>> def func():
... a = 1
... b = 2
... c = 3
... d = a + b + c
...
>>> func.__code__.co_stacksize
2
这里的值为 2,因为要求和a + b + c
,它首先加载a
and b
在堆栈上(LOAD_FAST https://docs.python.org/3/library/dis.html#opcode-LOAD_FAST) 并执行求和(BINARY_ADD https://docs.python.org/3/library/dis.html#opcode-BINARY_ADD) 并将结果放回堆栈顶部,现在c
被加载并与总和的结果相加a
and b
。因此,特定于此函数的堆栈需要的最大大小为 2。
1: The flag X:Frames
can be used in IronPython to enable CPython like frame objects.