Python 的 yield 关键字是做什么的?
yield 把普通函数变成生成器函数:每次执行到 yield 产出一个值并暂停,下次迭代从暂停处继续,适合处理大数据集而无需全部载入内存。
一句话解释:yield 让函数变成"生成器"——调用它不立即执行函数体,而是返回一个可迭代对象;每次 next()(或 for 循环)执行到下一个 yield 吐出一个值并挂起,状态保留,下次继续。相比 return 一次给全部结果,yield 逐个产出、惰性计算,处理海量数据时内存友好。
先理解可迭代与生成器
列表是可迭代的,所有元素都在内存里:
nums = [1, 10, 100, 1000]
for i in nums:
print(i)
生成器也是可迭代的,但元素是"现算现给"的,只能完整遍历一次:
gen = (x + 1 for x in range(4)) # 生成器表达式
for i in gen:
print(i)
yield 的基本用法
def count_up_to(n):
i = 1
while i <= n:
yield i # 产出 i,函数在此挂起
i += 1
for x in count_up_to(3):
print(x) # 依次输出 1、2、3
调用 count_up_to(3) 不会执行循环体,而是返回生成器对象;每次迭代推进到下一个 yield。
典型场景:逐行处理大日志文件
def read_large_log(path):
with open(path, encoding='utf-8', errors='replace') as f:
for line in f:
yield line.rstrip('\n')
for line in read_large_log('app.log'):
if 'ERROR' in line:
print(line)
几十 GB 的文件也能逐行处理,避免累计全部行,但仍受最长单行、缓冲与保留状态影响——这正是日志分析、ETL 场景最常见的写法。
提前停止读取时应显式 close 生成器或用 contextlib.closing 管理,确保 with 内文件及时关闭;errors='replace' 会替换无法解码的数据,并非无损。
进阶:yield 还能接收值
gen.send(value) 可以把值送回生成器内部(yield 表达式的返回值),首次发送非None值前需先 next(gen) 或 send(None) 启动;日常数据处理中用得少,了解即可。
常见问题(FAQ)
Q:生成器能遍历多次吗?
A:不能。遍历完后生成器耗尽,再次迭代得到空结果;需要重新调用函数生成新的。
Q:yield 和 return 能同时出现在一个函数里吗?
A:可以。普通 def 函数里出现 yield 就是生成器函数,return 用于结束迭代,其值可通过 StopIteration 取得;async def 中出现 yield 则是异步生成器,只能用不带值的 return 结束。
Q:yield from 是什么?
A:yield from 子生成器 用于把产出工作委托给另一个可迭代对象,普通迭代时类似逐个yield,但还会转发send/throw/close并取得子生成器return值,不是完全等价。