
Python 中的迭代器对象设计为单次使用,无法自动重置;若需多次遍历,应分离「可迭代对象」与「迭代器」职责——前者实现 __iter__ 返回新迭代器,后者专注单次遍历逻辑。
python 中的迭代器对象设计为单次使用,无法自动重置;若需多次遍历,应分离「可迭代对象」与「迭代器」职责——前者实现 `__iter__` 返回新迭代器,后者专注单次遍历逻辑。
在 Python 的迭代协议中,迭代器(iterator)和可迭代对象(iterable)是两个明确区分的概念。许多初学者误将二者混为一谈,导致自定义类只能被遍历一次。根本原因在于:迭代器一旦抛出 StopIteration,就必须持续抛出该异常,不可重置状态重新开始(Python 官方文档明确要求)。因此,试图在同一个迭代器实例上调用多次 for 循环失败,并非 bug,而是协议强制约束。
✅ 正确做法:分离 iterable 与 iterator
你需要定义两个类:
- MyIterable:可迭代对象,实现 __iter__,每次调用都返回一个全新的迭代器实例;
- MyIterator:真正的迭代器,仅负责单次线性遍历,持有独立的状态(如当前索引)。
以下是符合规范、健壮且清晰的实现:
class MyIterable:
def __init__(self, my_list):
self.my_list = list(my_list) # 防止外部修改影响迭代一致性
def __iter__(self):
return MyIterator(self.my_list)
class MyIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.data):
raise StopIteration
item = self.data[self.index]
self.index += 1
return item✅ 使用示例:
obj = MyIterable([10, 20, 30])
# 第一次遍历
for x in obj:
print(x) # 输出: 10, 20, 30
# 第二次遍历 —— 完全正常!
for y in obj:
print(y) # 再次输出: 10, 20, 30? 关键点:MyIterable.__iter__() 每次都构造并返回 全新 的 MyIterator 实例,因此每次 for 循环都拥有独立的 index 状态,互不干扰。
⚠️ 常见错误与注意事项
- ❌ 错误模式:在 __iter__ 中直接返回 self(即把可迭代对象自身当作迭代器),会导致“只能遍历一次”;
- ❌ 不要尝试在 __next__ 中检测 StopIteration 后重置 self.index = 0 —— 这违反迭代器协议,可能引发不可预测行为(如 for 循环提前终止、list(iter_obj) 返回空列表等);
- ✅ 推荐防御性编程:在 MyIterator.__init__ 中对输入数据做浅拷贝(如 list(my_list)),避免外部修改原始列表导致迭代中途 IndexError 或逻辑错乱;
- ✅ 若逻辑简单,优先使用生成器函数替代手写迭代器类,更简洁、安全、符合 Python 惯例:
class MyIterable:
def __init__(self, my_list):
self.my_list = list(my_list)
def __iter__(self):
yield from self.my_list # 每次调用都创建新生成器✅ 总结
| 角色 | 职责 | 是否可重复使用 |
|---|---|---|
| 可迭代对象(Iterable) | 实现 __iter__(),返回新迭代器 | ✅ 是(每次 for 都触发新迭代器) |
| 迭代器(Iterator) | 实现 __iter__() 和 __next__(),维护单次遍历状态 | ❌ 否(协议禁止重置) |
牢记:“可迭代” ≠ “已迭代”。只要正确遵循迭代协议——让 __iter__ 返回新对象,你的自定义类型就能像 list、tuple、str 一样天然支持无限次遍历。










