在 Python 中,生成器是用函数实现的迭代器:函数体包含 yield 时,调用该函数得到生成器对象而非立即执行完毕。每次 next 运行至下一个 yield,交出值并暂停,局部状态保留,下次从暂停处继续。生成器适合按需产生大量或无限序列,而不必一次性装入列表。本节说明 yield 语义及与列表推导的对比。
含 yield 的函数
调用 my_gen() 时函数体不会一次跑完,仅返回生成器;实际执行发生在 next 时:
def my_gen():
yield 1
yield 2
yield 3
g = my_gen()
print(next(g))
print(next(g))
print(next(g))
# print(next(g)) # StopIteration
yield 暂停与恢复
遇到 yield 时函数暂停并返回一个值,保留现场;下次 next() 从暂停处继续,直至无 yield 可停则抛出 StopIteration。
def gen():
print("开始")
yield "A"
print("继续")
yield "B"
g = gen()
print(next(g))
print(next(g))
第一次 next 打印开始并返回 A;第二次从 yield "A" 之后继续,打印继续并返回 B。
无限序列
无法用有限列表表示的递增偶数序列,可用生成器边计算边产出:
def evens():
n = 0
while True:
yield n
n += 2
e = evens()
print(next(e))
print(next(e))
print(next(e))列表推导与生成器表达式
方括号列表推导会立即构造完整列表;圆括号生成器表达式为惰性。生成器用尽后不能再次遍历:
squares_list = [x * x for x in range(10000000)]
squares_gen = (x * x for x in range(10000000))
print(type(squares_list))
print(type(squares_gen))
print(sum(squares_gen))用 for 遍历生成器
def count_down(n):
while n > 0:
yield n
n -= 1
for x in count_down(3):
print(x, end=" ")
print()按行读取大文件
在生成器内对文件 for line in f,可将各行依次交给外层循环,避免 read() 整文件进内存:
def read_lines(path):
with open(path, "r", encoding="utf-8") as f:
for line in f:
yield line.strip()
for line in read_lines("demo.txt"):
print(line)生成器与手写迭代器类
| 维度 | 生成器 | 自定义迭代器类 |
|---|---|---|
| 写法 | 函数 + yield | 类 + __iter__ / __next__ |
| 代码量 | 通常更短 | 样板较多 |
| 适用 | 状态推进较直观 | 需要完整类接口时 |
常见错误
- 生成器同样只能完整消费一次,用尽后须重新调用生成器函数。
- 生成器函数中的
return主要用于结束迭代,返回值不会作为next的元素(与yield不同)。 - 函数体中若无
yield,调用结果为普通返回值,不是生成器。
本节说明了 Python 中生成器与 yield 的行为。下一节介绍装饰器:@ 语法及 functools.wraps。