Python 生成器和迭代器的区别
FreeGuideOnline
最新
2026-07-05
python
自定义一个简单的迭代器类
class CountDown: def init(self, start): self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current < 0:
raise StopIteration
val = self.current
self.current -= 1
return val
使用迭代器
counter = CountDown(3) for num in counter: print(num) # 输出 3 2 1 0
常见的可迭代对象(如列表、元组)并不是迭代器,但可以通过 `iter()` 函数获得它们的迭代器。
```python
lst = [1, 2, 3]
iterator = iter(lst)
print(next(iterator)) # 1
print(next(iterator)) # 2
什么是生成器(Generator)
生成器是一种特殊的迭代器,使用起来更简洁。Python 提供两种创建生成器的方式:
- 生成器函数:使用
yield关键字代替return。 - 生成器表达式:类似列表推导式,但使用圆括号。
生成器同样惰性求值,每次调用 next()(或在 for 循环中)会执行到下一个 yield 语句并暂停,保存当前状态,下次调用时从暂停处继续执行。
# 生成器函数
def countdown_gen(n):
while n >= 0:
yield n
n -= 1
# 生成器表达式
squares = (x * x for x in range(5))
# 使用生成器
cd = countdown_gen(3)
for num in cd:
print(num) # 输出 3 2 1 0
print(list(squares)) # [0, 1, 4, 9, 16]
生成器自动实现了 __iter__() 和 __next__() 方法,因此它就是一个迭代器。可以用 isinstance 验证:
from collections.abc import Generator, Iterator
gen = (x for x in range(3))
print(isinstance(gen, Generator)) # True
print(isinstance(gen, Iterator)) # True
生成器和迭代器的关键区别
| 对比维度 | 迭代器 (Iterator) | 生成器 (Generator) |
|---|---|---|
| 创建方式 | 实现 __iter__ 和 __next__ 的类 |
使用 yield 的函数或生成器表达式 |
| 代码量 | 需要明确维护状态(如 self.current) |
函数内局部变量自动保存,代码简洁 |
| 内存占用 | 由开发者控制,通常也是惰性求值 | 天然惰性求值,只在需要时产生一个值 |
| 可重用性 | 取决于实现,一般迭代器只能遍历一次 | 每个生成器对象只能遍历一次 |
| 状态管理 | 手动管理索引或状态变量 | yield 暂停时自动保存堆栈帧状态 |
| 功能完整性 | 可扩展其他方法,灵活性更高 | 功能单一,专为惰性生成序列设计 |
| 典型用途 | 自定义复杂遍历逻辑,读取文件、数据库游标等 | 处理大数据流、无限序列、管道式数据处理 |
内存开销与执行模型
迭代器完全可以预先存储所有数据(虽然不推荐),但生成器强制惰性求值。下面的例子对比了列表(可迭代但非迭代器)、普通迭代器与生成器的内存占用:
import sys
# 列表:一次性分配全部内存
lst = [i for i in range(1000000)]
print(sys.getsizeof(lst)) # 约 800 万字节
# 迭代器:依然可以基于已存在的数据,内存占用取决于底层数据结构
it = iter(lst) # 本身只是引用列表,额外内存很小
# 生成器表达式:几乎不占额外内存,按需计算
gen = (i for i in range(1000000))
print(sys.getsizeof(gen)) # 约 112 字节(与元素数量无关)
生成器函数也是同样,只有在执行到 yield 时才计算一个值,前面的局部变量暂时保留。
可重用性与单向性
大部分迭代器只能被遍历一次,因为 StopIteration 发生后,再次 next() 会直接抛出异常,除非在 __iter__ 中重新初始化状态。生成器同样一次性,遍历结束后无法重置。
gen = countdown_gen(2)
print(list(gen)) # [2, 1, 0]
print(list(gen)) # [] 因为已经耗尽
如果需要多次迭代,应该每次调用生成器函数返回一个新的生成器对象:
def gen_func():
yield 1
yield 2
for i in gen_func():
print(i) # 1 2
for i in gen_func():
print(i) # 1 2 (新对象,重新开始)
异常和高级特性
生成器除了 yield 产生值,还可以使用 send() 方法向生成器内部传递值,以及 throw() 和 close() 方法控制生成器行为。这是普通迭代器所没有的。
def receiver():
while True:
val = yield
print(f"Received: {val}")
gen = receiver()
next(gen) # 预激生成器
gen.send(10) # Received: 10
gen.send(20) # Received: 20
gen.close()
这使得生成器不仅可以产出数据,还可以用作协程。
何时使用生成器或迭代器
- 当需要定义一个简单的惰性序列,或者对大型数据集进行逐项处理时,首选生成器。代码更清晰,内存效率极高。
- 如果需要封装复杂的状态逻辑,或者需要提供额外的辅助方法(比如
current()、reset()等),或者需要维护多个相互关联的状态变量,自定义迭代器类会更合适。 - 在数据处理管道中,生成器可以像管道组件一样串联,利用生成器表达式实现流式处理:
nums = (x for x in range(100))
doubled = (x * 2 for x in nums)
filtered = (x for x in doubled if x > 50)
# 直到 list(filtered) 才真正开始计算
print(list(filtered))