直接实现__iter__和__next__比生成器更可控,因其支持动态响应外部状态、暴露pause()/reset()等控制接口,且能精确管理迭代状态与异常(如主动raise stopiteration),而生成器难以从外部干预执行流。

为什么直接实现 __iter__ 和 __next__ 比用生成器更可控?
因为你要在迭代过程中动态响应外部状态(比如实时读取传感器数据、按需加载分页 API、或根据条件提前终止),而生成器函数(yield)一旦启动就难以从外部干预其执行流。自定义类能暴露控制接口,比如 pause()、reset() 或检查 is_exhausted 属性。
常见错误是只实现 __next__ 却忘了让 __iter__ 返回 self,导致 for 循环报 TypeError: 'MyIterator' object is not iterable。
-
__iter__必须返回一个实现了__next__的对象(通常是self) -
__next__在无数据时必须显式抛出StopIteration,不能返回None或空字符串 - 如果想支持多次遍历,
__iter__不该每次都返回新实例(除非你刻意设计成“单次消费”)
如何正确处理边界和异常?
最常踩的坑是 __next__ 里没做边界判断,导致索引越界后抛出 IndexError,而不是预期的 StopIteration——这会让 for 循环意外中断并打印 traceback。
假设你封装一个列表的只读迭代器:
class ListIterator:
def __init__(self, data):
self.data = data
self.index = 0
<pre class="brush:python;toolbar:false;">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
- 永远优先用
if+raise StopIteration,而不是依赖下标访问失败 - 若底层数据可能被外部修改(如 list 被 append),需在
__next__中重新计算len(self.data),而非缓存长度 - 不要在
__next__中做耗时操作(如网络请求)而不加超时或重试,否则一次next()调用就卡死整个循环
什么时候该让 __iter__ 返回新实例?
当你需要支持「多个独立迭代器同时遍历同一对象」时,比如对一个文件对象反复调用 iter(f) 应该每次返回新读取位置的迭代器,而不是共享同一个指针。
典型场景:自定义容器类(如 Stack、PriorityQueue)允许同时被多个 for 循环使用,互不干扰。
- 返回新实例:每次
__iter__创建并返回一个干净的迭代器对象(含独立状态) - 返回
self:适合「单次线性消费」场景,如日志流、传感器采样流 - 混用风险:如果
__iter__返回self,但用户手动调用了多次iter(obj),所有迭代器会共享状态,结果不可预测
与内置类型行为对齐的关键细节
Python 的 list、dict 等在 for 中可重复遍历,是因为它们的 __iter__ 每次都返回新迭代器;而文件对象只能遍历一次,因为 file.__iter__ 返回自身。你的类是否符合用户直觉,取决于这个选择。
- 想模仿
list行为:在__iter__中return ListIterator(self.data) - 想模仿
open(...)行为:在__iter__中return self,并在__next__中维护内部游标 - 别忽略
iter(obj, sentinel)形式:它不调用__iter__,而是反复调用obj()直到返回sentinel,和你的类无关
真正难的是状态同步——比如你给迭代器加了 peek() 方法,那后续 next() 是否跳过已 peek 的项?这种逻辑必须在类内部严格闭环,外部没法帮你兜底。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











