yield 使函数执行上下文(含局部变量)挂起并封装进生成器栈帧,随生成器生命周期存活;该栈帧位于堆中,保存变量、指令指针等,通过 next() 激活恢复执行。

yield 不是让变量“常驻内存”,而是让函数的执行上下文(包括局部变量)被挂起并封装进生成器对象的栈帧中,随生成器生命周期存活。
生成器本质是一个带状态的迭代器对象
调用含 yield 的函数时,Python 并不执行函数体,而是立即返回一个 generator 对象。这个对象内部持有一个特殊的栈帧(frame),它不是普通函数调用后就销毁的临时栈帧,而是被长期保留、可反复激活的“冻结态执行环境”。
- 该栈帧完整保存了:局部变量值、指令指针位置(当前执行到哪条字节码)、代码对象引用、作用域链等
- 每次调用
next()或send(),解释器会“唤醒”这个栈帧,从中断处继续执行,直到下一个yield或函数结束 - 只要生成器对象没被垃圾回收,这个栈帧就一直存在——变量不是“常驻内存”,而是“绑定在栈帧生命周期内”
内存拓扑:栈帧 → 生成器对象 → 堆内存引用
从物理内存角度看,变量并未脱离栈帧独立驻留。真正的映射关系是:
- 生成器对象本身分配在堆上(heap),它持有一个指向其专属栈帧(
f_frame)的指针 - 该栈帧的局部变量区(
f_locals)也位于堆中(Python 的栈帧实际是堆分配的对象,非 C 栈) - 例如:
def countdown(n): while n > 0: yield n; n -= 1中的n,始终存于该生成器栈帧的f_locals字典里,随生成器存活 - 你可通过
gen.gi_frame.f_locals直接访问这些“冻结”的变量值(前提是生成器未关闭)
与普通函数的关键区别:栈帧不销毁
普通函数调用结束后,其栈帧被立即释放;而生成器函数的首次调用仅创建栈帧,却不执行——它进入 GEN_CREATED 状态。只有触发 next() 后才进入 GEN_RUNNING,并在每个 yield 处切换为 GEN_SUSPENDED,此时栈帧被挂起但未释放。
- 这种设计使局部变量自然延续生命周期,无需额外闭包或类封装
- 变量“看起来常驻”,实则是栈帧对象持续持有它们——这是 Python 解释器对生成器状态机的底层实现保障
- 一旦生成器耗尽或显式关闭(
gen.close()),栈帧被清空,变量随之不可访问
验证方式:直接观察栈帧状态
你可以用标准库工具窥探这一机制:
-
import inspect; inspect.getframeinfo(gen.gi_frame)查看当前挂起位置 -
gen.gi_frame.f_locals查看所有存活的局部变量 -
gen.gi_running和gen.gi_suspended反映状态机当前阶段 - 用
dis.dis(type(gen).__next__)可见底层YIELD_VALUE指令如何协作保存/恢复上下文

















