yield 是把一个普通函数变成 生成器函数 的关键字。它的核心作用是:产出一个值并“暂停”函数执行,保留当前局部状态;下次继续从暂停处往下执行。因此:
• return:一次性返回,函数结束。
• yield:返回一个值但不结束,下次迭代再继续(直到遇到 return 或运行到结尾)。
什么时候用 yield
1. 惰性计算、节省内存:一次产出一个值,而不是一次把所有结果装进列表。
2. 可中断/可恢复的流程:生成器在每次 yield 处暂停、恢复。
3. 数据管道:一段一段处理大文件/网络数据。
4. (进阶)协程风格:配合 .send() 向生成器“反向传值”。
5. (进阶)委托:用 yield from 把迭代转发给子生成器。
⸻
基础示例:把函数变成生成器
def squares(n):
for i in range(n):
yield i * i # 每次产出一个值并暂停
g = squares(3) # 只是创建生成器,不执行
next(g) # -> 0
next(g) # -> 1
next(g) # -> 4
再 next 会 StopIteration(生成器耗尽)
实际使用更常见的是:
for x in squares(5):
print(x)
流式处理/节省内存
def read_chunks(path, size=8192):
with open(path, "rb") as f:
while True:
buf = f.read(size)
if not buf:
return # 或者写 `break`,生成器结束
yield buf # 一块一块读,适合大文件
与 return 的关系
• 生成器里 return 会结束生成器;
• Python 3.3+ 允许 return value,这个 value 只能被 yield from 的调用方拿到(见下例)。
进阶一:send() 做“协程式”通信
你可以把值“发回去”给上一次 yield 表达式:
def averager():
total = count = 0
avg = None
while True:
x = yield avg # 程序在这里暂停,等待外部 send(x)
total += x
count += 1
avg = total / count
g = averager()
next(g) # 先“预热”,让生成器跑到第一个 yield
print(g.send(10)) # -> 10.0
print(g.send(20)) # -> 15.0
print(g.send(30)) # -> 20.0
注意:未启动的生成器不能直接 send(非 None),必须先 next(g) 或 g.send(None)。
进阶二:yield from 委托给子生成器(PEP 380)
• 扁平化子迭代器的 yield;
• 透传 .send()/.throw()/.close();
• 接收子生成器的 return value。
def sub():
yield 1
yield 2
return "done" # 返回值供外层拿
def outer():
result = yield from sub() # 把迭代委托给 sub()
print("sub returned:", result)
for _ in outer():
pass
迭代过程中会产出 1、2;结束时打印:sub returned: done
异步生成器(了解即可)
在 async def 里用 yield 会得到 异步生成器,需要 async for 消费:
import asyncio
async def ticker(n):
for i in range(n):
yield i
await asyncio.sleep(1)
async def main():
async for x in ticker(3):
print(x)
asyncio.run(main())
常见坑
• 只拿到生成器对象:忘了迭代它(for、next()、list())。
• 启动时 send:首次必须 next(g) 或 send(None)。
• 在 preload_app/多进程场景:把不可 fork 的资源(线程池/连接)放到启动钩子里,不要在定义生成器的模块顶层就执行。
• StopIteration 传播:不要在生成器内部显式 raise StopIteration(PEP 479 约束);用 return 结束。
• lambda 里不能用 yield。
⸻
小抄
• 想要“边算边用、少占内存” → 用 yield。
• 想把多个迭代器拼起来/代理 → 用 yield from。
• 想双向通信(把值送回生成器) → 用 .send();先预热。
• 需要把生成器结果一次性收集 → list(gen) 或 for 循环。
Python中yield关键字详解
1031

被折叠的 条评论
为什么被折叠?



