循环、迭代器、生成器
一、循环(Loop)
本质
循环是一种控制结构,用于重复执行代码块。它本身不是数据结构,而是一种语法机制。
常见形式
1  | # 1. for 循环(遍历可迭代对象)  | 
适用场景
- 需要重复执行相同或相似的操作
 - 遍历容器中的元素(如列表、字典)
 - 实现需要终止条件的逻辑(如用户登录验证)
 
优势
- 简单直观,易于理解和使用
 - 适用于大多数重复操作场景
 
劣势
- 当处理大量数据时,可能占用大量内存(如生成全量数据列表)
 - 无法暂停或恢复执行流程
 
二、迭代器(Iterator)
本质
迭代器是实现了迭代器协议的对象(即包含 __iter__() 和 __next__() 方法)。它是一种数据访问方式,允许你按需逐个访问元素。
核心方法
__iter__():返回迭代器自身__next__():返回下一个元素,若没有元素则抛出StopIteration异常
示例
1  | my_list = [1, 2, 3]  | 
适用场景
- 需要逐个处理大量数据(如文件读取、数据库查询结果)
 - 实现自定义的遍历逻辑(如树形结构遍历)
 
优势
- 节省内存:不需要预先生成所有元素,而是按需生成
 - 支持无限序列(如计数器、斐波那契数列)
 
劣势
- 只能向前遍历,不能回退或重置(除非重新创建迭代器)
 - 代码复杂度略高于普通循环
 
三、生成器(Generator)
本质
生成器是一种特殊的迭代器,它通过更简洁的语法实现(使用 yield 关键字或生成器表达式)。它允许你在需要时生成值,而不是预先计算并存储所有值。
两种形式
生成器函数(使用
yield):1
2
3
4
5
6
7def my_generator():
yield 1
yield 2
yield 3
gen = my_generator()
print(next(gen)) # 输出 1生成器表达式(类似列表推导式,但用圆括号):
1
2gen = (x for x in range(3))
print(next(gen)) # 输出 0
适用场景
- 处理大量数据或无限序列(如日志分析、大数据集)
 - 实现惰性计算(只在需要时生成结果)
 - 简化迭代器的实现(无需手动编写 
__iter__和__next__方法) 
优势
- 最节省内存:逐个生成值,不存储整个序列
 - 代码简洁,可读性高
 - 支持暂停和恢复执行(每次 
yield后暂停,下次调用next()继续执行) 
劣势
- 只能遍历一次,不能回头
 - 不适合需要随机访问元素的场景
 
四、对比总结
| 特性 | 循环(Loop) | 迭代器(Iterator) | 生成器(Generator) | 
|---|---|---|---|
| 本质 | 控制结构 | 实现迭代协议的对象 | 特殊的迭代器 | 
| 内存效率 | 低(预生成所有元素) | 高(按需生成) | 极高(按需生成) | 
| 代码复杂度 | 最低 | 中等 | 低(比迭代器更简洁) | 
| 状态保存 | 无 | 自动保存(通过 __next__) | 
自动保存(通过 yield) | 
| 适用场景 | 简单重复操作 | 自定义遍历逻辑 | 大数据处理、惰性计算 | 
| 能否无限序列 | 需手动控制终止条件 | 支持 | 支持 | 
五、何时用哪个?
- 用循环:当需要简单遍历已知的、有限的数据集,且不需要节省内存时。
 - 用迭代器:当需要自定义遍历逻辑,或处理大量数据但不想一次性加载到内存时。
 - 用生成器:当处理超大数据集、无限序列,或需要最大化内存效率时(如数据管道、流式处理)。
 
举个例子:
- 遍历一个小列表:直接用 
for循环。 - 遍历一个 10GB 的日志文件:用生成器逐行读取。
 - 实现一个斐波那契数列生成器:用生成器函数(无限序列)。
 
看完之后可能还会有一个疑问,就是__iter__返回自身,但是好像并没有看到调用__iter__的地方,这是为什么呢?
六、__iter__ 的核心作用
在Python中,迭代器协议要求对象必须同时实现两个方法:
__iter__():返回迭代器自身(即self)__next__():返回下一个元素,若无元素则抛出StopIteration
关键点:__iter__ 的存在是为了让迭代器对象也能作为可迭代对象使用。
七、为什么需要 __iter__ 返回自身?
1. 支持 for 循环和内置函数
Python的 for 循环、list()、sum() 等内置函数在使用迭代器前,会先调用 iter() 函数将对象转换为迭代器。例如:
1  | my_list = [1, 2, 3]  | 
关键点:当你将迭代器对象传入 for 循环时,Python会先调用 iter(iterator),此时需要 iterator.__iter__() 返回自身,否则会报错。
2. 支持多重迭代
有些场景需要对同一个迭代器进行多次迭代(虽然不常见)。例如:
1  | class MyRange:  | 
八、__iter__ 何时被调用?
- 显式调用:当你手动调用 
iter(iterator)时 - 隐式调用:
- 使用 
for循环遍历迭代器时 - 使用内置函数(如 
list()、sum()、tuple()等)处理迭代器时 - 在需要可迭代对象的地方使用迭代器时(如解包操作 
*iterator) 
 - 使用 
 
示例:
1  | my_iterator = iter([1, 2, 3]) # 创建迭代器  | 
九、为什么你没看到 __iter__ 被调用?
因为 __iter__ 通常是被Python解释器隐式调用的,你在代码中看不到它。但如果没有正确实现 __iter__,以下情况会报错:
1  | class BadIterator:  | 
十、__iter__ 的意义
- 满足迭代器协议:Python要求迭代器必须同时实现 
__iter__和__next__ - 支持与语言特性无缝集成:确保迭代器可以在 
for循环、内置函数等场景中正常工作 - 区分迭代器和可迭代对象:
- 可迭代对象(如列表):
__iter__返回一个新的迭代器 - 迭代器本身:
__iter__返回自身(self) 
 - 可迭代对象(如列表):
 
如果没有 __iter__,迭代器就无法作为可迭代对象使用,Python的许多语法和函数都将无法正常处理它。
看到迭代器跟生成器有这么复杂的功能,那么实际场景当中不免让人想,生成器跟迭代器在循环过程当中可以重置吗?可以反向找到上一个吗?
在 Python 中,生成器默认是单向、不可逆的,但可以通过一些技巧实现“回退”或“重置”的效果。让我分情况解释:
十一、标准生成器:无法回退或重置
生成器函数(使用 yield)或生成器表达式创建的生成器对象,只能向前迭代,无法后退或重置。每次调用 next() 会执行到下一个 yield,状态自动保存,直到生成结束(抛出 StopIteration)。
示例:
1  | def count_up_to(n):  | 
十二、实现“回退”功能的方法
1. 缓存已生成的值(手动回退)
将生成器的输出缓存到列表中,需要回退时直接访问列表。
示例:
1  | def count_up_to(n):  | 
2. 自定义生成器类(支持双向迭代)
通过实现 __iter__ 和 __next__ 方法,结合内部状态管理,实现双向迭代。
示例:
1  | class BidirectionalGenerator:  | 
十三、实现“重置”功能的方法
1. 每次创建新生成器实例
最简单的方法是每次需要重置时重新调用生成器函数。
示例:
1  | def count_up_to(n):  | 
2. 自定义可重置生成器类
通过添加 reset() 方法重置内部状态。
示例:
1  | class ResettableGenerator:  | 
十四、使用 tee 复制生成器(有限回退)
itertools.tee 可以将一个生成器复制为多个独立的迭代器,允许有限次数的回退。
示例:
1  | from itertools import tee  | 
注意:tee 会缓存已生成的值,若原生成器已消耗过多元素,可能导致内存问题。
十五、建议
| 需求 | 最佳实现方式 | 适用场景 | 
|---|---|---|
| 少量回退 | 缓存已生成的值(列表/队列) | 数据量较小,需频繁前后移动 | 
| 完全重置 | 重新创建生成器实例或自定义 reset() | 
需多次从头开始迭代 | 
| 多分支迭代 | itertools.tee | 
需要同时维护多个迭代进度 | 
核心原则:生成器的设计初衷是高效处理一次性数据流,若需要频繁回退或随机访问,建议使用列表等可重复遍历的数据结构。只有在数据量极大且确实需要回退时,才考虑上述优化方法。
看到以上案例,肯定还有人还有疑问,就是这不是在说生成器么,怎么生成器的class当中没有yield?没有yield的话,这是不是就是歌迭代器类了?不是生成器类了?马上回答这个问题
这涉及到 Python 中 生成器(Generator) 和 迭代器(Iterator) 的本质区别。
十六、核心概念区分
1. 生成器(Generator)
- 定义:一种特殊的迭代器,使用 
yield关键字 或 生成器表达式 创建。 - 特点:
- 自动实现 
__iter__和__next__方法。 - 状态自动保存(每次 
yield暂停,下次从暂停处继续)。 - 代码更简洁,无需手动管理状态。
 
 - 自动实现 
 
示例:
1  | def gen_numbers(): # 生成器函数(使用 yield)  | 
2. 迭代器(Iterator)
- 定义:实现了 
__iter__和__next__方法 的对象。 - 特点:
- 手动管理状态(如维护索引)。
 - 需显式抛出 
StopIteration。 - 代码更复杂,但更灵活。
 
 
示例:
1  | class NumIterator: # 迭代器类  | 
十七、我们提到的类属于迭代器,而非生成器
当我编写的类(如 ResettableGenerator 和 BidirectionalGenerator)没有使用 yield 关键字 时,它们实际上是 迭代器类,而非生成器类。虽然名字中包含“Generator”,但从技术上讲:
- 生成器:由 
yield或生成器表达式创建,是 Python 内置的特殊类型。 - 迭代器:通过手动实现 
__iter__和__next__方法创建的普通类。 
示例对比:
1  | # 生成器(使用 yield)  | 
十八、为什么会混淆?
- 生成器是迭代器的子类:所有生成器都是迭代器(因为自动实现了迭代器协议),但并非所有迭代器都是生成器。
 - 语义习惯:在日常交流中,有时会用“生成器”泛指任何可迭代对象,导致混淆。
 - 功能重叠:两者都支持 
next()和for循环,但实现方式不同。 
十九、何时用迭代器?何时用生成器?
| 场景 | 选择 | 示例 | 
|---|---|---|
| 简单的一次性数据流 | 生成器 | python<br>def read_file_lines(file_path):<br>    with open(file_path) as f:<br>        for line in f:<br>            yield line<br> | 
| 复杂状态管理(如双向迭代) | 迭代器 | 自定义类实现 prev() 或 reset() 方法 | 
| 需要实现特定接口(如上下文管理器) | 迭代器 | 类中同时实现 __iter__ 和 __enter__/__exit__ | 
二十、理解
- 生成器:用 
yield创建,自动实现迭代器协议,代码简洁。 - 迭代器:手动实现 
__iter__和__next__,更灵活但代码复杂。 - 选择标准:优先用生成器(代码简洁),需要高级控制时用迭代器。
 
当你需要“回退”或“重置”功能时,由于生成器本身不支持这些特性,只能通过迭代器类手动实现。虽然名称可能叫“Generator”,但本质上是迭代器。










