Python 生成器
I’m happy to help translate the article, but I don’t have the text of the post itself. Could you please paste the content you’d like translated (excluding the source link you already provided)? Once I have the text, I’ll translate it into Simplified Chinese while preserving the original formatting and code blocks.
生成器函数
生成器函数是一种特殊的函数,它返回一个 惰性迭代器。
这些对象可以像列表一样进行循环遍历,但与列表不同的是,它们 不会将内容全部存储在内存中。
当调用生成器函数时,它会返回一个 生成器对象。
函数内部的代码只有在调用生成器的 next()(或 __next__())方法时才会执行,一次产生一个值。
生成器表达式(生成器推导式)
生成器表达式看起来几乎与列表推导式相同,但它不是在内存中创建完整的列表,而是创建一个生成器对象,惰性地产生值。
列表推导式 vs. 生成器表达式
| 特性 | 列表推导式 | 生成器表达式 |
|---|---|---|
| 语法 | [x for x in ...] | (x for x in ...) |
| 内存使用 | 高 – 将所有项目存储在列表中 | 低 – 仅存储迭代器状态 |
| 执行 | 即时 – 所有值一次性计算 | 惰性 – 仅在需要时生成值 |
| 结果类型 | list | generator |
列表推导式(在内存中创建完整列表)
squares = [x * x for x in range(5)]
print(squares)
# Output: [0, 1, 4, 9, 16]
所有值会立即计算并存储在内存中。
生成器表达式(惰性求值)
squares = (x * x for x in range(5))
print(squares) # <generator object at 0x...>
尚未计算任何值。
只有在遍历生成器时才会生成值。
如何使用生成器表达式?
必须遍历它,例如:
for num in squares:
print(num)
或者将其转换为列表(这会强制求值):
print(list(squares))
内存使用示例(重要)
import sys
lst = [x for x in range(1_000_000)]
gen = (x for x in range(1_000_000))
print(sys.getsizeof(lst)) # large
print(sys.getsizeof(gen)) # small
生成器使用显著更少的内存,因为它不会一次性存储所有元素。
“不调用函数”
通常,你会使用函数来创建生成器:
def my_generator():
for x in range(5):
yield x * x
使用生成器表达式,你可以省略函数定义:
gen = (x * x for x in range(5))
非常常见的用例 – 直接传入函数
许多内置函数接受任意可迭代对象,因此可以直接将生成器表达式传入它们:
total = sum(x * x for x in range(1_000_000))
- 不需要额外的括号
- 内存高效
- 语法简洁
yield 语句
yield 类似于 return,但它不是终止函数,而是 暂停 函数,保存其状态并向调用者返回一个值。当生成器被恢复(通过 next() 或 for 循环)时,执行会在 yield 之后继续。
关键点
- 生成器的局部变量、指令指针和内部栈都会被保存。
- 可以使用多个
yield语句来生成一系列值。 return完全结束生成器,并抛出StopIteration。
何时使用生成器表达式?
- ✅ 大型数据集
- ✅ 流式数据
- ✅ 单遍迭代
- ✅ 对内存敏感的应用
在需要以下情况时请避免使用:
- ❌ 随机索引
- ❌ 对数据进行多次遍历
Source: …
惰性求值的内部工作原理
惰性求值意味着 仅在需要时 才计算值。在 Python 中,这通过 迭代器 和 生成器 实现。
急切求值 vs. 惰性求值(思维模型)
急切求值
data = [x * 2 for x in range(5)]
- 循环立即执行,所有值都被计算,并且列表被存入内存。
惰性求值
data = (x * 2 for x in range(5))
- 目前没有任何计算,只创建了一个生成器对象。
- 当迭代时,值会逐个产生。
生成器到底是什么
生成器本质上是一个 状态机:
- 在每个
yield处 暂停 执行。 - 保存 当前指令指针和局部变量。
- 返回 产生的值。
- 稍后从保存的位置 恢复 执行。
步骤演示
def squares():
for i in range(3):
yield i * i
g = squares() # 创建生成器,尚未执行任何代码
next(g) # → 0
next(g) # → 1
next(g) # → 4
next(g) # 抛出 StopIteration
每次调用 next(g) 都会恢复执行,直到下一个 yield,随后再次暂停。
为什么生成器是内存高效的
range(1_000_000)只存储start、stop和step。(x * x for x in range(1_000_000))存储对 range 的引用、当前索引以及执行状态。
因此 内存使用保持恒定,不论生成的项目数量多少。
内置函数的惰性求值
| 函数 | 惰性? |
|---|---|
range() | ✅ |
map() | ✅ |
filter() | ✅ |
zip() | ✅ |
sum() | ❌ (消耗迭代器) |
示例
m = map(lambda x: x * x, range(10))
# 在迭代 `m` 之前不会进行任何计算。
StopIteration 如何结束惰性求值
当生成器结束时,Python 会抛出 StopIteration。迭代协议(例如 for 循环)会捕获此异常并停止循环。
gen = (x for x in range(3))
list(gen) # [0, 1, 2]
list(gen) # [] (generator is exhausted)
生成器是 单遍 的;一旦到达末尾,除非创建新的生成器,否则无法重新回到开头。
Python 生成器 – 高级控制方法
生成器让你惰性地产生值,在每个 yield 处暂停执行。除了简单的迭代,Python 还提供了三种控制方法,允许你从外部与生成器交互:
| 方法 | 用途 |
|---|---|
next() | 恢复生成器并返回下一个产生的值 |
send(value) | 恢复 并 发送一个值,该值成为最近一次 yield 表达式的结果 |
throw(exception) | 恢复并在生成器暂停的地方抛出异常 |
close() | 优雅地终止生成器(在内部抛出 GeneratorExit) |
1. .send(value) – 向生成器发送数据
通常生成器只向外产生值。
send() 将一个值注入生成器,返回给最近的 yield 表达式。
def counter():
value = yield 0 # first yield
while True:
value = yield value + 1
gen = counter()
print(next(gen)) # start generator → yields 0
print(gen.send(10)) # sends 10 → yields 11
print(gen.send(20)) # sends 20 → yields 21
关键规则
- 第一次调用必须是
next(gen)或gen.send(None)。 send(x)将x赋给最近的yield表达式。
2. .throw(exception) – 在生成器内部抛出异常
throw() 在生成器暂停的点注入异常。
如果生成器捕获了该异常,它可以处理;否则异常会向外传播。
def generator():
try:
while True:
yield "running"
except ValueError:
yield "ValueError handled"
gen = generator()
print(next(gen)) # → running
print(gen.throw(ValueError)) # → ValueError handled
典型使用场景
- 取消正在进行的工作
- 发出错误信号
- 中断长时间运行的生成器
3. .close() – 优雅地停止生成器
调用 close() 会在生成器内部抛出 GeneratorExit,让它在 finally 块中执行必要的清理工作。
def generator():
try:
while True:
yield "working"
finally:
print("Cleaning up resources")
gen = generator()
print(next(gen)) # → working
gen.close() # triggers cleanup
输出
working
Cleaning up resources
生命周期摘要
| 方法 | 效果 |
|---|---|
next() | 恢复生成器 |
send(x) | 恢复 + 发送值(x 成为最近一次 yield 的结果) |
throw(e) | 恢复 + 在暂停点抛出异常(e) |
close() | 终止生成器(抛出 GeneratorExit) |
总结
Python 中的 生成器 是一种函数,它:
- 使用
yield关键字。 - 一次产生一个 值。
- 在每次执行之间记住其局部状态。
当 Python 遇到 yield 时,它:
- 返回 产生的值给调用者。
- 暂停 执行,保存当前的局部状态。
- 从该确切位置恢复,在下一次迭代时(或在调用
send、throw或close时)。