
Python 迭代器 与生成器深度解析与避坑指南
- 🟢 第一部分:迭代器 (Iterator)
-
- ✅ 1. 核心定义与机制
- ✅ 2. 语法与实例
-
- A. 内置函数操作
- B. 自定义迭代器类
- ⚠️ 3. 致命陷阱
-
- 陷阱一:迭代器耗尽 (Exhaustion)
- 陷阱二:手动遍历忘记处理 StopIteration
- 陷阱三:修改正在迭代的容器
- 🟡 第二部分:生成器 (Generator)
-
- ✅ 1. 核心定义与机制
- ✅ 2. 语法与实例
-
- A. 生成器函数 (`yield`)
- B. 生成器表达式
- C. `yield from` (Python 3.3+)
- ⚠️ 3. 致命陷阱
-
- 陷阱一:生成器只能遍历一次
- 陷阱二:`return` 在生成器中的作用
- 陷阱三:可变默认参数陷阱 (闭包问题)
- 陷阱四:生成器中的异常传播
- 🔵 第三部分:常用操作运算符与核心陷阱
-
- 1. `next()` 函数
- 2. `in` 运算符 (成员检测)
- 3. `*` 解包运算符
- 4. `yield from` vs 循环 `yield`
- 5. `send()` 和 `throw()` (高级操作)
- 🏆 四、总结速查表 & 专家建议
-
- 💡 总结速查表
- 💡 专家建议
这是 Python 迭代器 (Iterator) 与生成器 (Generator) 深度解析与避坑指南 (2026版)。
这两个概念是 Python 处理大数据流、实现惰性计算(Lazy Evaluation)以及构建高效管道(Pipeline)的核心基石。理解它们的区别与联系,是进阶 Python 开发的必经之路。
🟢 第一部分:迭代器 (Iterator)
✅ 1. 核心定义与机制
- 定义:迭代器是一个实现了 迭代器协议 (Iterator Protocol) 的对象。
- 协议要求:必须实现两个方法:
- __iter__(): 返回迭代器对象本身(使迭代器可被 for 循环使用)。
- __next__(): 返回下一个元素。如果没有更多元素,抛出 StopIteration 异常。
- 特性:
- 单向性:只能向前,不能后退(无法重置,除非重新创建)。
- 惰性:数据不是一次性加载到内存,而是按需计算。
- 状态保持:内部记录当前遍历的位置。
✅ 2. 语法与实例
A. 内置函数操作
my_list = [1, 2, 3]
it = iter(my_list) # 获取迭代器 (调用 list.__iter__())
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
# print(next(it)) # ❌ 抛出 StopIteration 异常
B. 自定义迭代器类
实现一个倒序迭代器:
class ReverseIter:
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index -= 1
return self.data[self.index]
rev = ReverseIter([1, 2, 3])
for x in rev:
print(x)
# 输出: 3, 2, 1
⚠️ 3. 致命陷阱
陷阱一:迭代器耗尽 (Exhaustion)
迭代器是一次性的。一旦遍历完成,它就“空”了。
data = [1, 2, 3]
it = iter(data)
list(it) # [1, 2, 3] (消耗完毕)
list(it) # [] (再次获取为空!)
sum(it) # 0
对策:如果需要多次遍历,请保留原始列表/元组,或每次重新调用 iter()。
陷阱二:手动遍历忘记处理 StopIteration
在使用 while 循环和 next() 时,必须捕获异常。
# ❌ 错误:程序崩溃
it = iter([1])
while True:
print(next(it)) # 第二次调用时报错
# ✅ 正确
try:
while True:
print(next(it))
except StopIteration:
pass
陷阱三:修改正在迭代的容器
在遍历列表时直接修改其长度(增删元素),会导致行为不可预测或跳过元素。
nums = [1, 2, 3, 4]
for n in nums:
if n == 2:
nums.remove(n) # ❌ 危险:可能导致跳过 '3'
print(nums)
对策:遍历副本 for n in nums[:],或使用列表推导式生成新列表。
🟡 第二部分:生成器 (Generator)
✅ 1. 核心定义与机制
- 定义:生成器是一种特殊的迭代器,它让编写迭代器变得极其简单。
- 两种创建方式:
- 生成器函数:使用 yield 关键字的函数。
- 生成器表达式:类似列表推导式,但用圆括号 ()。
- 工作原理:
- 调用生成器函数不执行代码,而是返回一个生成器对象。
- 每次调用 next(),函数从上次 yield 处恢复执行,直到遇到下一个 yield。
- 局部变量状态在暂停期间被保存。
✅ 2. 语法与实例
A. 生成器函数 (yield)
def fibonacci(limit):
a, b = 0, 1
count = 0
while count < limit:
yield a # 暂停,返回 a
a, b = b, a + b # 下次从这里继续
count += 1
gen = fibonacci(5)
print(next(gen)) # 0
print(next(gen)) # 1
# 或者
for num in fibonacci(5):
print(num, end=" ") # 0 1 1 2 3
B. 生成器表达式
# 列表推导式 (立即执行,占内存)
squares_list = [x**2 for x in range(1000000)]
# 生成器表达式 (惰性,几乎不占内存)
squares_gen = (x**2 for x in range(1000000))
print(next(squares_gen)) # 0
print(next(squares_gen)) # 1
C. yield from (Python 3.3+)
用于简化嵌套生成器,直接委托给子生成器。
def chain(*iterables):
for it in iterables:
yield from it # 等价于 for item in it: yield item
for x in chain([1, 2], ['a', 'b']):
print(x) # 1, 2, a, b
⚠️ 3. 致命陷阱
陷阱一:生成器只能遍历一次
同迭代器,生成器也是“一次性”的。
gen = (x for x in range(3))
print(list(gen)) # [0, 1, 2]
print(list(gen)) # [] (已耗尽)
陷阱二:return 在生成器中的作用
在生成器中,return value 会终止生成器并抛出 StopIteration(value)。
- 在普通 for 循环中,这个返回值会被忽略。
- 只有在通过 yield from 委托时,返回值才能被外部捕获。
def gen():
yield 1
return "Done" # 终止生成器
g = gen()
print(next(g)) # 1
# next(g) -> 抛出 StopIteration,其 value 属性为 "Done"
陷阱三:可变默认参数陷阱 (闭包问题)
生成器函数也是函数,同样受默认参数可变性影响。
def gen_accumulator(seq=[]): # ❌ 默认列表在所有实例间共享
for item in seq:
yield item
seq.append(99) # 副作用
g1 = gen_accumulator([1])
g2 = gen_accumulator([2])
list(g1) # [1]
list(g2) # [2, 99] (因为 g1 修改了共享的默认列表!)
对策:默认参数设为 None,内部初始化。
陷阱四:生成器中的异常传播
如果生成器内部出错,迭代中断,且生成器状态变为“关闭”,无法恢复。
def bad_gen():
yield 1
raise ValueError("Boom")
yield 2 # 永远达不到
g = bad_gen()
print(next(g)) # 1
# next(g) -> 抛出 ValueError,生成器彻底关闭
🔵 第三部分:常用操作运算符与核心陷阱
在迭代器和生成器的操作中,以下运算符和函数至关重要:
1. next() 函数
- 核心:获取下一个值。
- 陷阱:
- 不提供默认值时,耗尽会抛异常。
- 最佳实践:提供默认值避免异常。
val = next(gen, None) # 耗尽时返回 None,不报错
2. in 运算符 (成员检测)
- 核心:检查元素是否存在。
- 陷阱:消耗性。
- 对生成器使用 x in gen 会一直迭代直到找到 x 或耗尽。
- 如果没找到,生成器就废了;如果找到了,生成器停在找到的位置之后,前面的没了。
gen = (x for x in range(10))
print(5 in gen) # True (消耗了 0-5)
print(next(gen)) # 6 (0-5 已经丢失,无法回头)
3. * 解包运算符
- 核心:将迭代器/生成器展开。
- 陷阱:内存爆炸。
- list(gen) 或 [*gen] 会强制完全消费生成器并将所有数据加载到内存。如果生成器代表无限流或大数据,会导致 OOM (Out Of Memory)。
# ❌ 危险:如果 big_data_gen 产生 10 亿条数据
all_data = [*big_data_gen()]# ✅ 安全:逐个处理
for item in big_data_gen():
process(item)
4. yield from vs 循环 yield
- 核心:委托子迭代器。
- 陷阱:性能与代码清晰度。
- 手动 for x in sub: yield x 效率略低且代码冗长。
- yield from sub 经过 C 层优化,速度更快,且能正确处理 send(), throw(), close() 等高级交互。
5. send() 和 throw() (高级操作)
生成器不仅是被动迭代,还可以双向通信。
- gen.send(value): 将值发送给生成器,作为当前 yield 表达式的结果。
- gen.throw(exc): 在生成器内部抛出异常。
- 陷阱:
- 首次启动:必须先调用 next(gen) 或 gen.send(None) 启动生成器,否则 send(value) 会报错。
def echo():
while True:
received = yield
print(f"Got: {received}")e = echo()
# e.send("Hello") # ❌ TypeError: can't send non-None value to a just-started generator
next(e) # ✅ 启动
e.send("Hello") # 输出: Got: Hello
🏆 四、总结速查表 & 专家建议
💡 总结速查表
| 定义方式 | 类实现 __iter__ + __next__ | 函数含 yield 或 表达式 () |
| 代码量 | 多 (需维护状态变量) | 极少 (自动维护状态) |
| 内存效率 | 高 (惰性) | 极高 (惰性) |
| 可复用性 | 否 (一次性) | 否 (一次性) |
| 主要用途 | 自定义复杂遍历逻辑 | 数据流处理、管道、大文件读取 |
| 常见陷阱 | 忘记处理 StopIteration | 误以为可多次遍历;send 未启动 |
| 性能 | 快 | 极快 (C 优化) |
💡 专家建议
with open(file_path) as f:
for line in f:
yield line.strip()
# 内存友好:一次只处理一行
for line in read_large_file("huge.log"):
process(line)
data = (int(x) for x in raw_lines if x.isdigit())
squared = (x**2 for x in data)
result = sum(squared) # 整个过程中没有创建任何中间列表
掌握迭代器与生成器,你就掌握了 Python 处理海量数据和构建高效异步逻辑的钥匙!






