yield使函数变为生成器,返回generator对象而非直接执行;首次调用next()或for循环时才运行至首个yield暂停并返回值;支持send()实现双向通信,yield from可委托子生成器并转发控制流。

yield 不是 return,它让函数变成生成器
调用含 yield 的函数不会立即执行函数体,而是返回一个 generator 对象。只有在第一次调用 __next__() 或进入 for 循环时,代码才从头开始运行,直到遇到第一个 yield 暂停,并把右边的值返回。
常见错误现象:TypeError: 'generator' object is not callable —— 误以为生成器还能像函数一样再加括号调用;或者 StopIteration 突然冒出来,只因没意识到生成器只能遍历一次。
- 生成器函数返回的是迭代器,不是结果值;想看内容得用
list(gen)或循环展开 -
yield后面可以跟任意表达式,包括None;不写等价于yield None - 函数里只要出现一次
yield,整个函数就变成生成器函数,哪怕还有return(return仅用于提前结束并触发StopIteration)
yield 表达式能接收 send() 传进来的值
yield 不只是语句,它本身是个表达式,可以赋值、参与运算。配合 generator.send(value),就能实现双向通信——这是协程最基础的机制。
使用场景:需要外部控制生成节奏,比如流式处理中根据下游反馈决定是否继续读取下一批数据。
立即学习“Python免费学习笔记(深入)”;
- 首次调用
send()必须传None,否则报TypeError: can't send non-None value to a just-started generator - 每次
send()都会唤醒生成器,把值赋给当前yield左边的变量,然后继续往下执行到下一个yield - 如果生成器已结束(抛出
StopIteration),再send()就会报StopIteration异常,不是静默失败
示例:
def echo():
while True:
x = yield
print("got:", x)调用 g = echo(); next(g); g.send("hello") 打印 got: hello。
yield from 是语法糖,但不是简单展开
yield from 主要用来委托子生成器,但它不只是把子生成器的每个 yield 拉平;它还会自动转发 send()、throw()、close(),并把子生成器的返回值暴露给外层。
容易踩的坑:yield from subgen 中,如果 subgen 是普通可迭代对象(如列表),它只是逐个 yield 元素;但如果 subgen 是生成器,那它就接管了控制流——你不能再对这个子生成器单独操作。
- 子生成器抛出的
StopIteration会被捕获,其value成为yield from表达式的返回值 - 外层生成器收到
throw()时,会先尝试发给子生成器;子生成器没处理才向上冒泡 - 不要在
yield from后面直接接return值——Python 3.3+ 允许,但语义易混淆;推荐用变量承接返回值再return
生成器和协程的边界在哪
纯生成器只响应 next() 和 send();而协程(async def 函数)必须用 await 调度,底层依赖事件循环。两者都“暂停/恢复”,但调度机制完全不同。
性能影响:生成器几乎零开销,适合内存敏感的流式处理;协程有事件循环调度成本,但支持真正的并发 I/O。
- 别试图用
yield写异步逻辑——没有事件循环,time.sleep()还是会阻塞整个线程 -
async def函数里不能用yield(除非加@types.coroutine手动标记,但极少必要) - 老项目里混用
yield和async/await容易掉进“看起来像协程、其实还是同步”的陷阱
真正容易被忽略的是:生成器的状态完全由 Python 解释器维护在帧对象里,一旦被垃圾回收,状态就永久丢失——所以别依赖长期存活的未消费生成器。










