yield 将函数变为生成器工厂,调用返回 generator 对象而非执行结果;与 return 的本质区别在于:return 终止函数并丢弃局部变量,yield 暂停函数并完整保留栈帧(含局部变量和执行位置),下次从 yield 下一行继续。

yield 是什么,它和 return 有什么本质区别
yield 不是“暂停函数”,而是把函数变成生成器工厂——调用它返回的是一个 generator 对象,不是执行结果。真正执行从第一次 next() 或进入 for 循环才开始,每次遇到 yield 就交出一个值并挂起状态。
和 return 最关键的区别:return 终止函数、丢弃局部变量;yield 暂停函数、保留整个栈帧(包括所有局部变量、执行位置)。下一次恢复时,代码从 yield 下一行继续运行。
常见错误现象:print(my_generator) 只显示 <generator object ...>,不触发任何计算;误以为写了个 yield 就等于“已经算出了值”。
什么时候必须用 yield 而不是列表推导式
核心判断标准:数据集大小超过可用内存,或上游消费者是逐步消费的(比如流式读取日志、分页 API 响应、实时传感器数据)。
立即学习“Python免费学习笔记(深入)”;
典型场景:
- 读取超大文件(GB 级),逐行处理但不想一次性
readlines() - 数据库游标遍历百万级记录,避免
fetchall()把整张表拉进内存 - 递归生成无限序列(如斐波那契、素数流),根本无法存成列表
性能影响:用 list(range(10**7)) 占约 80MB 内存;等价的 (i for i in range(10**7)) 或用 yield 实现的生成器,内存占用恒定在几百 KB 级别。
yield from 怎么简化嵌套生成器委托
yield from 不是语法糖,它会自动处理子生成器的 StopIteration、传递 send()/throw() 调用,省去手动 for 循环 yield 的样板代码。
错误写法:for x in subgen(): yield x —— 看似等价,但无法正确转发异常和协程信号。
正确用法示例:
def chain(*iterables):
for it in iterables:
yield from it # ✅ 自动委托迭代、异常、关闭
<p>def read_large_file(path):
with open(path) as f:
yield from f # ✅ 直接委托文件对象的迭代器,比 for line in f: yield line 更健壮
兼容性注意:yield from 是 Python 3.3+ 特性,2.x 或老项目需规避。
生成器被意外耗尽后为什么不能再用
生成器对象是一次性的:一旦抛出 StopIteration,它就处于“已关闭”状态,后续任何 next() 都会立即再抛 StopIteration,不会重跑逻辑。
容易踩的坑:
- 把同一个生成器对象传给多个
for循环,第二个循环直接跳过 - 误用
list(gen)后还想再用gen迭代 - 在函数中返回
my_generator(),却没意识到每次调用才新建生成器,而不是复用
调试技巧:打印 gen.gi_frame(Python 3.6+)可观察是否为 None(表示已结束);更稳妥的做法是每次需要迭代时,重新调用生成器函数,而非复用 generator 对象。
复杂点在于:生成器的状态封装太深,不像列表那样可反复遍历;它的价值恰恰来自这种单次、可控、低开销的流转控制——想“重播”,就得明确重建。


















