迭代器 vs 可迭代对象
一、一句话区分
-
可迭代对象 Iterable:能被
for循环“逐个拿出来”的东西(实现了__iter__)。你可以反复向它要一个“发牌员”。 -
迭代器 Iterator:真正“一个一个吐出来”的状态机(同时实现
__iter__返回自身 +__next__取下一个)。
生活化比喻:Iterable 像“一叠牌”,Iterable 自己不会发牌;Iterator 像“发牌员”,他手上记着发到哪一张了,你每次喊
next()他就发下一张,发完就摊手说没了(StopIteration)。
二、Duck Typing 判断标准
| 角色 | 必须实现的方法 | 含义 |
|---|---|---|
| 可迭代对象 Iterable | __iter__()(返回迭代器) | “我能给你一个发牌员” |
| 迭代器 Iterator | __iter__()(返回 self)+ __next__() | “我就是发牌员,每次发一张” |
迭代器一定是可迭代对象(因为也有
__iter__),但可迭代对象不一定是迭代器(list 不是迭代器)。
三、关系与执行流程
graph LR A["for x in obj"] --> B["it = iter(obj)"] B --> C{obj 有 __iter__?} C -->|是| D["拿到 Iterator"] C -->|否 旧式 __getitem__| D D --> E["next(it) 每次取一个"] E -->|还有元素| D E -->|没了| F(["抛出 StopIteration\nfor 循环捕获后退出"])
四、可运行代码示例
from collections.abc import Iterable, Iterator
class CountDown:
def __init__(self, n):
self.n = n
def __iter__(self): # 可迭代对象:返回迭代器(这里返回自身)
return self
def __next__(self): # 迭代器:返回下一项
if self.n <= 0:
raise StopIteration
self.n -= 1
return self.n
cd = CountDown(3)
print(isinstance(cd, Iterable)) # True
print(isinstance(cd, Iterator)) # True(因为 __iter__ 返回 self)
for x in cd:
print(x) # 2 1 0
# list 是可迭代对象,但不是迭代器
lst = [1, 2, 3]
print(isinstance(lst, Iterable)) # True
print(isinstance(lst, Iterator)) # False
it = iter(lst) # 用内置 iter() 拿到迭代器
print(isinstance(it, Iterator)) # True
print(next(it), next(it)) # 1 2
五、核心考点
-
迭代器是“一次性”的:数据被“消费”后耗尽,再
next()直接StopIteration。想重新遍历需重新iter(obj)。 -
生成器(generator)一定是迭代器,但迭代器不一定是生成器——生成器只是写起来更省事的一种迭代器。
-
for循环底层:先it = iter(obj),再不断next(it),捕获StopIteration后悄悄退出。 -
为什么
list不是迭代器:列表要保持“可重复遍历”,而迭代器会记住进度并消费数据(像网络流、大文件只能读一次,就必须用迭代器/生成器)。
六、对比速查表
| 可迭代对象 Iterable | 迭代器 Iterator | |
|---|---|---|
| 必须方法 | __iter__ | __iter__ + __next__ |
能被 for 遍历 | ✅ | ✅ |
| 是否记住进度 | ❌ | ✅ |
| 能否重复遍历 | ✅ | ❌(一次性) |
| 典型例子 | list / dict / str / set | iter(list)、生成器 |
速记卡(面试闪卡)
Q1:一句话讲清「迭代器 vs 可迭代对象」到底是什么?
A:可迭代对象(Iterable)有 iter,迭代器(Iterator)额外有 next,是能逐个吐数据的状态机。
Q2:一、一句话区分 —— 怎么理解?
A:Iterable 像”一叠牌”,自己不会发牌,只能给你一个发牌员;Iterator 像”发牌员”(Dealer),手上记着发到哪张,你每次喊 next() 它就发下一张,发完抛 StopIteration。Iterable 实现了 iter,Iterator 额外实现 next(迭代器协议,Iterator Protocol)。
Q3:二、Duck Typing 判断标准 —— 怎么理解?
A:判断只看”会不会这两招”:Iterable 必须有 iter()(返回迭代器),“我能给你一个发牌员”;Iterator 必须有 iter()(返回 self)+ next(),“我就是发牌员”。所以迭代器一定是可迭代对象(鸭子类型,Duck Typing),但 list 有 iter 没 next,得用 iter(list) 才拿到迭代器。
Q4:三、关系与执行流程 —— 怎么理解?
A:for x in obj 底层先 it = iter(obj) 拿到迭代器,再不断 next(it),捕获 StopIteration 后悄悄退出——像自动按铃的发牌机。任何能被 for 遍历的东西(生成器、range、文件)最终都靠 next 一个一个给值,理解这点就通了。
Q5:四、可运行代码示例 —— 怎么理解?
A:CountDown 类 iter 返回 self、next 返回下一项并在到 0 时抛 StopIteration,所以它既是 Iterable 也是 Iterator。list 是 Iterable 但不是 Iterator——list 要保持可重复遍历,故意不做成”一次性”状态机,需要时 iter(list) 才生成新迭代器(惰性求值,Lazy Evaluation)。
Q6:核心速记主线有哪些?
-
可迭代对象 Iterable 有 iter,迭代器 Iterator 有 iter + next
-
迭代器一定是 Iterable,但 list 不是迭代器
-
for 底层 = iter() + next() + 捕获 StopIteration
-
迭代器一次性消费、list 可重复;生成器必是迭代器
口诀
A:Iterable 一叠牌,Iterator 发牌员;
前者 iter 给,后者 next 添;
迭代器一次性,StopIteration 断;
list 非迭代器,iter() 才见面。
相关链接
-
📋 目录:00-Python
-
📚 学习清单:八股文学习路线图
-
🔗 列表推导式vs生成器表达式 — 生成器表达式返回的就是迭代器
-
🔗 生成器与yield原理 — 生成器是最常用的迭代器
-
🔗 yield from委托生成器 — 委托给另一个迭代器
-
🔗 设计模式八股文 — 迭代器模式在 OOP 中的定位
-
🔗 操作系统八股文 — 迭代/遍历与底层数据结构的关联