深入理解 Python 中的 yield:惰性计算与生成器的机制
在 Python 中,yield 是一个功能强大的关键字,常用于生成器(generator)函数的定义。它使函数在执行过程中可以“暂停”并返回值,而不是像普通函数那样一旦返回就终止。这种机制支持“惰性求值”,使我们能够以更高效、更节省内存的方式处理数据。
本文将系统讲解 yield 的基本概念、使用方法、和背后的运行机制。
什么是 yield?
简单来说,yield 用于将一个普通函数变成生成器函数。
一个包含 yield 的函数在调用时并不会立刻执行,而是返回一个生成器对象。只有在使用 next() 或在 for 循环中迭代这个生成器对象时,函数体才会真正执行,直到遇到第一个 yield 表达式。
每次遇到 yield,函数会暂停执行,并返回对应的值。下一次调用 next() 时,会从上次暂停的地方继续执行,而不是从头开始。
yield 与 return 的区别
| 特性 | return | yield |
|---|---|---|
| 函数执行状态 | 立即终止函数 | 暂停函数执行,保留当前状态 |
| 返回结果类型 | 普通值(如整数、字符串、列表等) | 生成器对象(generator) |
| 返回值数量 | 一次性返回一个结果 | 每次返回一个结果,可多次返回 |
| 内存占用 | 一次性产生所有结果,可能占用较大内存 | 逐个生成结果,节省内存,适合处理大数据集 |
一个简单的例子
def my_gen():
print("Start")
yield 1
print("Middle")
yield 2
print("End")
gen = my_gen() # 创建生成器对象,不执行函数体
print(next(gen)) # 执行到第一个 yield,输出 Start 和 1
print(next(gen)) # 接着执行到第二个 yield,输出 Middle 和 2
print(next(gen)) # 执行到函数结束,输出 End,抛出 StopIteration
输出结果:
Start
1
Middle
2
End
Traceback (most recent call last):
StopIteration
注意:只有在调用 next() 时,函数体才会执行。
for 循环是如何与 yield 协作的?
当我们使用 for value in my_gen(): 进行遍历时,Python 会自动调用生成器的 __next__() 方法。
其底层执行过程相当于:
gen = my_gen()
while True:
try:
value = next(gen)
print(value)
except StopIteration:
break
每次循环都从生成器中提取一个值,直到函数结束并抛出 StopIteration 异常为止。
yield 的典型应用场景
1. 按行搜索文件内容(节省内存)
from collections import deque
def search(lines, pattern, history=5):
previous_lines = deque(maxlen=history)
for line in lines:
if pattern in line:
yield line, previous_lines
previous_lines.append(line)
这种方式不需要一次性读取整个文件,而是逐行处理内容,适合日志搜索、大文件分析等场景。
2. 构建无限序列(如斐波那契数列)
def fib():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
调用:
f = fib()
for _ in range(10):
print(next(f))
输出前十个斐波那契数。由于是无限生成,不会浪费内存。
常见误区
误区 1:调用生成器函数会立即执行
实际情况是:调用带 yield 的函数只会返回一个生成器对象,并不会立即执行函数体。例如:
gen = my_gen() # 不执行函数体
只有当你调用 next(gen) 或使用 for 循环遍历时,才会开始执行函数体。
误区 2:yield 会自动推进函数执行
实际上,yield 本身并不会控制函数执行流程,它只是定义了函数可以暂停的位置。控制函数“继续执行”的是外部调用者——比如 for 循环或者 next() 函数。
总结
yield用于构建生成器函数,实现惰性求值;- 每次遇到
yield,函数会暂停并返回一个值; - 下一次执行会从上一次暂停处继续;
- 相比一次性返回所有结果,生成器可以显著减少内存消耗;
- 非常适合处理大文件、数据流和无限序列等场景;
for循环是驱动生成器最常用的方式,其本质是不断调用next()。
推荐阅读
- Python 官方文档:Generators
yield from的使用方法(用于委托另一个生成器)- 异步生成器(结合 async 使用)

浙公网安备 33010602011771号