yield 将函数变为生成器工厂,调用返回生成器对象而非执行函数体;首次 next() 启动执行,遇 yield 暂停并保存状态,后续 next() 从中断处继续,直至 stopiteration。

yield 不是简单地“暂停函数”,而是让函数变成生成器工厂——调用它不执行代码,只返回一个生成器对象;真正执行从第一次 next() 开始,遇到 yield 就交出值并挂起,保存全部局部状态(变量、执行位置、栈帧);下次继续从挂起点向下运行,直到下一个 yield 或函数结束抛出 StopIteration。
yield 如何把普通函数变成生成器
定义含 yield 的函数时,Python 解释器自动将其标记为 generator function。此时调用该函数(如 gen = my_func())不会运行函数体,而是立即返回一个 generator object。这个对象实现了迭代器协议(有 __iter__ 和 __next__ 方法),但内部尚未开始执行。
- 生成器对象是惰性的:不调用 next(gen) 或用 for 循环触发,函数体一行都不会执行
- 函数中只要出现 yield(哪怕还有 return 或在条件分支里),整个函数就属于生成器函数
- return 在生成器中可用于提前终止,并可带一个值(该值成为 StopIteration.value,但不会被 for 循环捕获)
每次 next() 发生了什么
首次调用 next(gen),解释器才真正进入函数体,逐行执行,直到遇到第一个 yield 表达式。此时:
– 计算 yield 右侧的值(如 yield x * 2),作为本次迭代的返回值
– 函数暂停,所有局部变量、指令指针、调用栈信息被完整保存在生成器对象内部
– 控制权交还给调用方
- 后续每次 next(),都从上次暂停的位置继续执行(不是重新开始)
- 如果遇到下一个 yield,流程同上;如果执行到函数末尾或碰到 return,则抛出 StopIteration 异常
- 生成器对象内部维护一个状态机(GEN_CREATED → GEN_RUNNING → GEN_SUSPENDED → GEN_CLOSED),确保挂起/恢复语义严格可靠
yield 表达式本身可接收外部传入的值
当使用 gen.send(value)(而非 next())时,传入的 value 会成为上一次 yield 表达式的返回值。这使得生成器具备双向通信能力:
- 第一次调用 send() 必须传 None(或直接用 next()),因为初始时没有“上一个 yield”可接收值
- yield 既是语句(交出值)也是表达式(产生值),所以可以写成 x = yield y
- 这种机制是协程(coroutine)的基础,async/await 的底层调度也依赖类似原理
生成器退出后状态不可恢复
一旦生成器因 StopIteration 终止(即状态变为 GEN_CLOSED),它就永远不能再被迭代。再次调用 next() 会直接抛出 StopIteration,且无法重置。
- 想重复使用,必须重新调用生成器函数创建新对象(gen = my_func())
- 生成器对象不支持 len()、索引或切片,因为它不预先计算全部结果,也没有长度概念
- 内存友好性正源于此:每个值按需生成、用完即弃,适合处理大数据流或无限序列
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











