Python 生成器

发布: (2026年2月21日 GMT+8 10:49)
10 分钟阅读
原文: Dev.to

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 ...)
内存使用 – 将所有项目存储在列表中 – 仅存储迭代器状态
执行即时 – 所有值一次性计算惰性 – 仅在需要时生成值
结果类型listgenerator

列表推导式(在内存中创建完整列表)

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))
  • 目前没有任何计算,只创建了一个生成器对象。
  • 当迭代时,值会逐个产生。

生成器到底是什么

生成器本质上是一个 状态机

  1. 在每个 yield暂停 执行。
  2. 保存 当前指令指针和局部变量。
  3. 返回 产生的值。
  4. 稍后从保存的位置 恢复 执行。

步骤演示

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) 只存储 startstopstep
  • (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 时,它:

  1. 返回 产生的值给调用者。
  2. 暂停 执行,保存当前的局部状态。
  3. 从该确切位置恢复,在下一次迭代时(或在调用 sendthrowclose 时)。
0 浏览
Back to Blog

相关文章

阅读更多 »

Python 检查数字是否为整数

python def is_intx: int | float | str | None: ''' 如果 x 表示整数值则返回 True,否则返回 False。支持:- int、float 和数值字符串,例如....'''