Python 的迭代器和生成器有什么区别?
核心概念
- 可迭代对象(Iterable):实现了
__iter__()方法,可以被for循环遍历,如list、str、dict - 迭代器(Iterator):同时实现了
__iter__()和__next__()方法,能逐个产出值 - 生成器(Generator):迭代器的一种,用
yield或生成器表达式创建,是迭代器的子集
可迭代对象(Iterable)
└── 迭代器(Iterator)
└── 生成器(Generator)
对比总览
| 迭代器 | 生成器 | |
|---|---|---|
| 创建方式 | 实现 __iter__ + __next__ 的类 | yield 函数或 () 表达式 |
| 代码量 | 较多(需写类) | 极少 |
| 状态保存 | 手动管理实例属性 | 自动保存执行帧 |
| 值的存储 | 可预先存储所有值 | 惰性计算,按需产出 |
| 是否是迭代器 | ✅ | ✅(生成器也是迭代器) |
自定义迭代器(类实现)
class CountUp:
def __init__(self, n):
self.n = n
self.current = 0
def __iter__(self):
return self # 返回自身
def __next__(self):
if self.current >= self.n:
raise StopIteration
val = self.current
self.current += 1
return val
for x in CountUp(3):
print(x) # 0 1 2
等价的生成器实现
def count_up(n):
for i in range(n):
yield i
for x in count_up(3):
print(x) # 0 1 2
同样的功能,生成器写法更简洁,且自动保存执行状态,无需手动管理 current。
关键区别详解
1. 值的生产方式
# 迭代器可以包装已有数据
class ListIterator:
def __init__(self, data):
self.data = data # 数据已全部在内存中
self.index = 0
def __iter__(self): return self
def __next__(self):
if self.index >= len(self.data):
raise StopIteration
val = self.data[self.index]
self.index += 1
return val
# 生成器惰性计算,不预先存储数据
def gen_range(n):
i = 0
while i < n:
yield i # 每次调用才计算下一个值
i += 1
2. 内存使用
import sys
# 列表迭代器:列表本身占内存,迭代器只是索引指针
lst = list(range(1_000_000))
it = iter(lst)
print(sys.getsizeof(lst)) # ~8 MB(数据在列表中)
print(sys.getsizeof(it)) # ~48 字节(只存索引)
# 生成器:几乎不占额外内存
gen = (x for x in range(1_000_000))
print(sys.getsizeof(gen)) # ~112 字节
3. 能否重置
gen = (x for x in range(3))
print(list(gen)) # [0, 1, 2]
print(list(gen)) # [] ← 生成器耗尽后无法重置
# 可迭代对象(如列表)可以重复迭代
lst = [0, 1, 2]
print(list(iter(lst))) # [0, 1, 2]
print(list(iter(lst))) # [0, 1, 2] ← 每次 iter() 产生新迭代器
验证关系
from collections.abc import Iterator, Generator
import inspect
def my_gen():
yield 1
gen = my_gen()
print(isinstance(gen, Iterator)) # True —— 生成器是迭代器
print(isinstance(gen, Generator)) # True —— 生成器是 Generator
print(inspect.isgenerator(gen)) # True
何时选择哪种
| 场景 | 推荐 |
|---|---|
| 简单的惰性序列 | 生成器表达式 () |
| 复杂逻辑的惰性序列 | 生成器函数 yield |
需要双向通信(.send()) | 生成器函数 |
| 需要多种迭代行为 / 复杂状态 | 自定义迭代器类 |
| 包装现有数据结构 | 自定义迭代器类 |
总结
- 生成器是迭代器的子集,所有生成器都是迭代器,反之不成立
- 生成器用
yield自动实现迭代器协议,代码更简洁 - 迭代器类适合需要精细控制状态或复杂行为的场景
- 两者都是一次性的:耗尽后无法重置,需重新创建