Python: 迭代器与生成器
最后更新:2026-09-22
你已经在用
for x in list:遍历数据了,但有没有想过in后面可以跟哪些东西?range(1000000)为什么不占内存?迭代器和生成器就是答案。它们让你能处理"无限"的数据流——按需生成,不占内存。
1. 什么是迭代器协议
Python 中,任何可以被 for 循环遍历的对象都可迭代(iterable)。它们背后实现了迭代器协议:
▶ 示例:迭代器协议基础
▶ 试一试输出:
# 执行成功
for 循环本质上就是:
▶ 示例:手动模拟 for 循环
▶ 试一试输出:
# 执行成功
2. 自定义迭代器
实现 __iter__ 和 __next__ 方法,让你的类能用 for 遍历:
▶ 示例:倒计时迭代器
▶ 试一试输出:
# 函数定义成功
▶ 示例:斐波那契迭代器(难度⭐⭐)
▶ 试一试输出:
# 函数定义成功
3. 生成器:yield
写一个类实现 __iter__ 和 __next__ 挺麻烦的。生成器函数用 yield 就能搞定同样的事:
▶ 示例:倒计时生成器
▶ 试一试输出:
# 函数定义成功
yield 和 return 不同——return 结束函数,而 yield 暂停函数,记住当前状态,下次调用时从暂停处继续。
▶ 示例:斐波那契生成器
▶ 试一试输出:
# 函数定义成功
4. 生成器的懒加载特性
生成器最大的优势是——不一次性生成所有数据,按需生产:
▶ 示例:生成器懒加载
▶ 试一试输出:
生成 1
生成 2
生成 3
创建了生成器,但还没执行
看到执行过程了吧?每次 next() 才执行到下一个 yield,然后停住。这个特性在处理大数据时非常有用:
▶ 示例:逐行读取大文件
# 处理大文件的"懒加载"方式
def read_large_file(filename):
"""逐行读取大文件,不一次性加载到内存"""
with open(filename, "r", encoding="utf-8") as f:
for line in f:
yield line.strip()
# 用生成器处理——每次只处理一行
for line in read_large_file("huge_log.txt"):
if "ERROR" in line:
print(line) # 处理速度只受磁盘读取速度限制
5. 生成器表达式
和列表推导式很像,但用圆括号——生成器表达式是惰性求值的:
▶ 示例:生成器表达式
▶ 试一试输出:
# 执行成功
| 对比 | 列表推导式 [] |
生成器表达式 () |
|---|---|---|
| 内存 | 所有数据在内存中 | 按需生成,几乎不占 |
| 速度 | 生成快,后续访问快 | 生成惰性,每次计算 |
| 可复用 | 可以反复遍历 | 只能遍历一次 |
| 适用场景 | 数据量小、需要反复使用 | 数据量大、只需遍历一次 |
6. 常见应用场景
- 大数据处理:逐行读取日志、CSV 文件,避免 OOM。
- 无限序列:生成器可以表示"无限"的数据流——如斐波那契数列、传感器数据。
- 管道处理:多个生成器串联,形成数据处理流水线。
- 反向遍历:
reversed()对某些类型返回迭代器。 - 惰性求值:函数返回生成器,调用方决定取多少个数据。
❓ 常见问题
return 一次性返回结果,函数结束。生成器用 yield 逐个产生值,每次暂停保存状态,下次从暂停处继续。生成器函数每次调用返回一个新的生成器对象。list(gen) 把生成器转成列表。但如果数据量太大,转列表会耗尽内存。折中方案:用 itertools.tee() 复制生成器,或者重新调用生成器函数生成一个新的。yield 和 return 可以共存吗?return 在生成器中相当于"停止迭代"并返回 StopIteration 的值。不过很少这么用。实践中如果你发现一个函数既要 yield 又要 return,最好想想设计是否合理。📖 小节
- 可迭代对象支持
for遍历,背后是__iter__和__next__协议 - 手动迭代:
iter()获取迭代器,next()逐个取值,StopIteration结束 - 生成器函数用
yield逐个产生值,状态自动保存 - 生成器是懒加载的——用多少产多少,不占内存
- 生成器表达式
(x for x in seq)类似列表推导式,但惰性求值 yield from可以委托另一个生成器(高级用法,后面用到再学)
📝 作业
-
基础题(难度⭐):用生成器写一个
even_numbers(max_n)函数,生成从 0 到max_n之间的所有偶数。 -
进阶题(难度⭐⭐):用生成器写一个
repeat_list(items, times)函数,把列表中的每个元素重复times次。例如list(repeat_list([1, 2, 3], 2))返回[1, 1, 2, 2, 3, 3]。 -
挑战题(难度⭐⭐⭐):写一个生成器
deep_flatten(nested),可以展开任意深度的嵌套列表。例如list(deep_flatten([1, [2, [3, 4]], 5]))返回[1, 2, 3, 4, 5]。提示:用for遍历元素,如果元素是列表则递归调用yield from。