2025-10-06 Python不基础 9——迭代器与生成器
文章目录
- 1. 迭代器(Iterator)
- 1.1. Iterable vs. Iterator
- 1.2. for loop 执行流程
- 1.3. 自定义可迭代对象(链表)
- 1.4. 迭代器为何要实现 `__iter__()`?
- 2. 生成器(Generator)
- 2.1. `yield` 关键字
- 2.2. 生成器 vs 迭代器
- 2.3. `send()` 方法
- 2.4. `return` 关键字
本文参考视频链接:
- https://www.bilibili.com/video/BV1ca411t7A9
- https://www.bilibili.com/video/BV1KS4y1D7Qb
1. 迭代器(Iterator)
在 Python 中,for loop
能遍历多种数据结构(如 list
、dict
、文件对象),看似直观,实则依赖两个核心概念——可迭代对象(Iterable) 和 迭代器(Iterator)。例如:
list
是有序结构,可按索引遍历;dict
是无序结构,无索引却能被遍历;- 文件对象是复杂结构,无“第 N 个元素”概念,仍能被遍历。
这些现象的本质是:所有能被 for loop
遍历的对象,都是“可迭代对象”,而 for loop
的底层是通过“迭代器”实现元素逐个获取的。
1.1. Iterable vs. Iterator
要理解 for loop
的实现,必须先明确两者的定义、区别与联系。
概念 | 官方定义(Python 文档) | 典型示例 |
---|---|---|
可迭代对象(Iterable) | 能“逐个返回自身成员”的对象,支持在 for loop 中使用,本质是“可产生迭代器的对象”。 | list 、str 、dict 、文件对象 |
迭代器(Iterator) | 表示“数据流”的对象,支持通过 next() 函数逐个获取数据,遍历到末尾时抛出 StopIteration 异常。 | iter(list) 返回的对象、文件迭代器 |
维度 | 可迭代对象(Iterable) | 迭代器(Iterator) |
---|---|---|
角色定位 | 数据的“保存者/容器”(如 list 存储元素) | 数据的“遍历者/状态持有者”(记录遍历进度) |
状态性 | 无状态(不知道遍历到哪一步,仅存储数据) | 有状态(明确知道当前遍历位置,下次从哪里继续) |
核心能力 | 能产生迭代器(通过 iter() 函数) | 能通过 next() 函数返回下一个元素 |
容器接口 | 通常实现容器接口(如 list 可通过索引修改元素) | 无容器接口(仅负责遍历,不支持修改数据) |
Python 通过“魔术方法”区分两者,具体要求如下:
概念 | 必须实现的魔术方法 | 作用说明 |
---|---|---|
可迭代对象(Iterable) | 满足以下任一条件: 1. 实现 __iter__() 方法(返回一个迭代器)2. 实现 __getitem__() 方法(支持按索引访问,如序列) | __iter__() :核心接口,通过 iter(iterable) 调用,返回迭代器;__getitem__() :兼容旧序列类型(如自定义序列)。 |
迭代器(Iterator) | 必须同时实现: 1. __next__() 方法(返回下一个元素,末尾抛 StopIteration )2. __iter__() 方法(返回自身,使迭代器也是可迭代对象) | __next__() :核心接口,通过 next(iterator) 调用,获取下一个元素;__iter__() :官方要求,确保迭代器可被 for loop 直接遍历。 |

1.2. for loop 执行流程
for loop
的本质是“自动获取迭代器 → 循环调用 next()
→ 捕获 StopIteration
终止”,具体步骤如下:
- 获取迭代器:对
for loop
中in
后的“可迭代对象”调用iter(iterable)
,生成对应的“迭代器”(记为it
)。- 底层字节码层面:通过
GET_ITER
指令完成(视频中提及的get iter
操作)。
- 底层字节码层面:通过
- 循环获取元素:反复调用
next(it)
,将返回值赋值给for loop
的循环变量(如for item in ...
中的item
)。 - 终止循环:当迭代器遍历到末尾时,
next(it)
抛出StopIteration
异常,for loop
捕获该异常并自动终止(无需手动处理)。
以下代码模拟了 for loop
的底层逻辑(等价于 for item in [1,2,3]
):
# 1. 获取可迭代对象
iterable = [1, 2, 3]
# 2. 生成迭代器
it = iter(iterable) # 调用 iterable.__iter__()
# 3. 循环调用 next() 并捕获异常
try:while True:item = next(it) # 调用 it.__next__()print(item) # 模拟 for loop 内的逻辑
except StopIteration:# 遍历结束,自动退出循环pass
1.3. 自定义可迭代对象(链表)
Python 无内置链表(link list
),但可通过“可迭代对象 + 迭代器”实现,使其支持 for loop
遍历。
- 链表由“节点(Node)”组成,每个节点包含
name
(数据)和next_node
(下一个节点的引用); - 要求:通过
for node in 链表头节点
,遍历所有节点并获取name
。
① 链表节点类(可迭代对象:Iterable)
需实现 __iter__()
方法,返回链表的迭代器。
class Node:def __init__(self, name):self.name = name # 节点数据self.next_node = None # 指向下一个节点的引用# 关键:实现 __iter__(),使 Node 成为可迭代对象def __iter__(self):# 返回链表的迭代器(NodeIterator),传入当前节点(链表头)return NodeIterator(self)
② 链表迭代器类(Iterator)
需实现 __next__()
(核心遍历逻辑)和 __iter__()
(满足官方要求)。
class NodeIterator:def __init__(self, head_node):# 初始化:当前遍历到的节点(从链表头开始)self.current_node = head_node# 核心:实现 __next__(),返回下一个节点def __next__(self):# 1. 若 current_node 为 None,说明遍历结束,抛出 StopIterationif self.current_node is None:raise StopIteration# 2. 保存当前节点(待返回)current = self.current_node# 3. 移动到下一个节点(更新状态)self.current_node = self.current_node.next_node# 4. 返回当前节点return current# 关键:实现 __iter__(),返回自身,使迭代器也是可迭代对象def __iter__(self):return self
③ 测试过程与结果
# 1. 构建链表:node1 → node2 → node3
node1 = Node("node1")
node2 = Node("node2")
node3 = Node("node3")
node1.next_node = node2 # node1 的下一个是 node2
node2.next_node = node3 # node2 的下一个是 node3# 2. 用 for loop 遍历链表(node1 是可迭代对象)
for node in node1:print(node.name) # 输出:node1 → node2 → node3

- 迭代器状态:
NodeIterator
通过current_node
记录遍历进度,每次调用__next__()
后更新为下一个节点; - 终止条件:当
current_node
为None
(链表末尾),抛出StopIteration
,for loop
自动终止; - 可迭代性:
Node
实现__iter__()
成为可迭代对象,NodeIterator
实现__iter__()
成为可迭代对象,确保能被for loop
遍历。
1.4. 迭代器为何要实现 __iter__()
?
为何迭代器必须实现 __iter__()
?,需从“官方要求”和“实际场景”两方面解释。
① 官方要求:迭代器必须是可迭代对象
Python 官方文档规定:迭代器(Iterator)必须实现 __iter__()
方法,且返回自身。
原因:确保迭代器能被用于所有“需要可迭代对象”的场景(如 for loop
、zip()
、map()
等),符合“直觉一致性”。

例如,若迭代器未实现 __iter__()
,直接放入 for loop
会报错:
# 错误案例:迭代器未实现 __iter__()
class BadIterator:def __init__(self, data):self.data = dataself.index = 0def __next__(self):if self.index >= len(self.data):raise StopIterationval = self.data[self.index]self.index += 1return val# 生成错误的迭代器
bad_it = BadIterator([1,2,3])
# 尝试用 for loop 遍历:报错(BadIterator 不是可迭代对象)
for val in bad_it: # TypeError: 'BadIterator' object is not iterableprint(val)
② 修正方案:__iter__()
返回自身
只需在迭代器中添加 __iter__()
并返回 self
,即可解决问题:
class GoodIterator:def __init__(self, data):self.data = dataself.index = 0def __next__(self):if self.index >= len(self.data):raise StopIterationval = self.data[self.index]self.index += 1return val# 关键:实现 __iter__(),返回自身def __iter__(self):return self# 生成正确的迭代器
good_it = GoodIterator([1,2,3])
# 可直接用 for loop 遍历:正常输出 1 → 2 → 3
for val in good_it:print(val)
CPython 本身并非完全遵循“迭代器必须是可迭代对象”的要求,但这属于底层实现细节,日常开发中仍建议严格实现
__iter__()
,原因如下:
- 确保代码兼容性(跨 Python 解释器,如 PyPy、Jython);
- 符合 Python 设计哲学(“显式优于隐式”“一致性”);
- 避免因底层差异导致的潜在 Bug。
2. 生成器(Generator)
生成器的核心定位是**“特殊的迭代器”**——它完全遵循迭代器协议(支持 next()
调用、触发 StopIteration
终止),但在定义方式、状态管理上更简洁。
前置要求:建议先理解迭代器(__iter__()
/__next__()
、状态保存、StopIteration
),否则难以理解生成器的“简化本质”。
生成器与迭代器的核心共性(视频验证):
- 均可通过
for loop
遍历(如for i in generator: ...
); - 均可通过
next()
函数逐个获取元素; - 遍历结束时均会触发
StopIteration
异常(无需手动捕获)。
这是生成器最易混淆的两个概念,必须明确区分,具体差异如下:
概念 | 定义与特征 | 示例代码 | 关键说明 |
---|---|---|---|
生成器函数 | 1. 用 def 定义,但包含 yield 关键字;2. 编译期被 Python 标记为“生成器函数”(非普通函数); 3. 调用后不执行函数体,而是返回生成器对象。 | def gen(n): yield n | 本质是“生成器的模板”,负责定义元素生成逻辑。 |
生成器对象 | 1. 调用生成器函数的返回值(如 g = gen(5) );2. 实现迭代器协议,可通过 next() /for 遍历;3. 保存函数运行状态(暂停位置、局部变量)。 | g = gen(5) | 本质是“可迭代的对象”,负责执行生成逻辑并返回元素。 |
- 普通函数调用:
def add(a,b): return a+b
→ 调用add(1,2)
直接返回结果3
; - 生成器函数调用:
def gen(n): yield n
→ 调用gen(5)
不返回5
,而是返回生成器对象g
,仅当调用next(g)
时才执行函数体。
2.1. yield
关键字
yield
是生成器的“灵魂”,其核心作用是**“暂停函数执行、保存状态、返回当前值”**,替代了传统迭代器中 __next__()
的手动状态管理。
# 生成器函数
def gen(n):while n > 0:yield n # 暂停点:返回n,保存状态n -= 1# 隐含 return,触发 StopIteration# 1. 调用生成器函数:生成生成器对象,不执行函数体
g = gen(5)
print(type(g)) # 输出:<class 'generator'>(非函数,非int)# 2. 第一次调用 next():执行函数体直到 yield
print(next(g)) # 输出:5(执行到 yield 5,暂停,n 仍为5)# 3. 第二次调用 next():从暂停处继续执行
print(next(g)) # 输出:4(先执行 n -=1 → n=4,再执行 yield 4,暂停)# 4. for loop 遍历剩余元素(等价于反复调用 next())
for i in g:print(i) # 输出:3 → 2 → 1(遍历结束后触发 StopIteration)

执行流程总结:
- 初始化:
g = gen(5)
→ 创建生成器对象,保存参数n=5
,函数体未执行; - 首次
next()
:执行函数体→进入while
循环→yield 5
→返回5
,暂停于yield
处(记住当前n=5
、循环状态); - 后续
next()
:从暂停处恢复→执行n -=1
→再次进入循环→yield n
→返回新值,再次暂停; - 终止:当
n=0
时退出while
循环→执行隐含的return
→触发StopIteration
→for
循环捕获异常并终止。
生成器的“暂停/恢复”依赖 Python 字节码中的 YIELD_VALUE
指令,核心逻辑:
- 执行到
YIELD_VALUE
时:- 将当前要返回的值(如
5
)存入“返回值缓冲区”; - 保存当前函数帧(
frame
)的状态(包括局部变量n
、程序计数器“下一条要执行的指令”); - 退出函数帧,返回值给
next()
调用者;
- 将当前要返回的值(如
- 下次
next()
时:- 恢复之前保存的函数帧状态;
- 从“下一条指令”继续执行(如
n -=1
)。
简言之:yield
替代了传统迭代器中“手动用实例变量保存状态”的逻辑,让 Python 自动管理函数运行状态。

2.2. 生成器 vs 迭代器
以“链表遍历”为例,传统迭代器需定义类并实现 __iter__()
/__next__()
,而生成器仅需用 yield
即可实现相同功能。
class Node:def __init__(self, name):self.name = nameself.next_node = Nonedef __iter__(self):# 生成器函数:自动实现迭代器协议current = self # 局部变量保存状态(无需实例变量)while current is not None:yield current # 暂停并返回当前节点,自动保存状态current = current.next_node # 恢复后更新状态# 测试(与传统迭代器完全一致)
node1 = Node("node1")
node2 = Node("node2")
node3 = Node("node3")
node1.next_node = node2
node2.next_node = node3for node in node1:print(node.name) # 输出:node1 → node2 → node3
维度 | 传统迭代器 | 生成器 |
---|---|---|
代码量 | 需定义两个类(可迭代对象+迭代器),代码冗余 | 仅需一个生成器函数(yield ),代码简洁 |
状态管理 | 需手动用实例变量保存状态(如 self.current ) | Python 自动通过函数帧保存状态(局部变量、暂停位置) |
迭代器协议实现 | 需手动实现 __iter__() /__next__() | 自动实现(生成器对象内置这两个方法) |
可读性 | 逻辑分散(类间跳转),不易理解 | 逻辑集中(一个函数内),符合直觉 |
2.3. send()
方法
send()
是生成器独有的功能(普通迭代器无此方法),核心作用是**“在迭代过程中向生成器传递值,改变其内部状态”**,实现“双向通信”。
send()
与 next()
的关系:
next(g)
等价于g.send(None)
(首次调用生成器时,必须传递None
,否则报错);g.send(value)
:在恢复生成器执行时,将value
作为yield
表达式的返回值,赋值给变量。
# 支持 send() 的生成器函数
def gen(n):while n > 0:# yield 表达式:接收 send() 传递的值,赋值给 temptemp = yield n if temp is not None: # 若传递了值,更新 nn = tempn -= 1# 1. 初始化生成器对象
g = gen(5)# 2. 首次调用:必须用 send(None) 或 next()
first = g.send(None) # 等价于 next(g)
print(first) # 输出:5(执行到 yield 5,暂停,temp 未赋值)# 3. 传递值给生成器:改变 n 的值
second = g.send(10) # 恢复执行→temp=10→n=10→n-=1=9→yield 9→返回9
print(second) # 输出:9# 4. for loop 遍历(等价于反复调用 g.send(None))
for i in g:print(i) # 输出:8 → 7 → ... → 1

- 首次调用限制:生成器未执行到
yield
时(初始化后),send()
不能传递非None
值,否则报错(因为没有yield
表达式接收值); yield
无赋值时的处理:若yield
未赋值给变量(如yield n
而非temp = yield n
),send()
传递的值会被忽略,但生成器仍会正常恢复执行;- 用途:用于动态调整生成器的内部逻辑(如视频中修改
n
的值,改变迭代次数),实现“外部控制生成器”。
2.4. return
关键字
生成器中的 return
与普通函数完全不同,核心规则:
- 触发
StopIteration
:生成器中执行return
(无论是否带值),都会立即触发StopIteration
异常,终止迭代; - 返回值的获取方式:
return
的值不会直接返回给next()
/send()
,而是作为StopIteration
异常的value
属性,需通过捕获异常获取。
def gen(n):while n > 0:yield nn -= 1return "迭代结束" # return 值存入 StopIteration.valueg = gen(3)try:while True:print(next(g)) # 输出:3 → 2 → 1
except StopIteration as e:print(e.value) # 输出:"迭代结束"(获取 return 值)