完整迭代器必须同时实现__iter__和__next__:__iter__需返回含__next__的对象(通常为self),__next__耗尽时须显式抛StopIteration;状态应存于实例属性而非类属性;支持多次迭代需每次__iter__返回新实例;生成器函数可更安全地替代手动实现。

__iter__ 和 __next__ 必须同时存在才能算完整迭代器
只实现 __iter__ 返回自身,却不定义 __next__,对象无法被 next() 调用,会抛出 TypeError: iterator does not have a __next__() method。反之,仅实现 __next__ 而没实现 __iter__,则无法用于 for 循环或 list() 等消费迭代器的场景,因为这些操作内部先调用 iter(),而 iter(obj) 要求对象必须有 __iter__ 方法(且返回一个迭代器)。
完整实现的关键是:
-
__iter__必须返回一个实现了__next__的对象(通常是self) -
__next__必须在耗尽时显式抛出StopIteration,不能靠返回None或静默退出 - 多次调用
iter()应该能获得独立的迭代状态(除非你刻意设计为单次可迭代)
手动管理状态时容易忽略的边界情况
比如实现一个从 0 数到 n 的计数器迭代器,常见错误是把索引初始化写在类属性里,导致所有实例共享状态:
class BadCounter:
i = 0 # ❌ 类变量,所有实例共用
def __init__(self, n):
self.n = n
def __iter__(self):
return self
def __next__(self):
if self.i >= self.n:
raise StopIteration
val = self.i
self.i += 1
return val正确做法是把状态放在实例属性中:
class GoodCounter:
def __init__(self, n):
self.n = n
self.i = 0 # ✅ 实例变量
def __iter__(self):
return self
def __next__(self):
if self.i >= self.n:
raise StopIteration
val = self.i
self.i += 1
return val另一个易错点:__next__ 中未处理 self.i 超出范围的瞬间(比如 n=0 时首次调用就该抛异常),会导致无限循环或越界访问。
立即学习“Python免费学习笔记(深入)”;
支持多次迭代需返回新迭代器实例
如果希望同一个对象能被多次 for 遍历(比如 list、range 的行为),就不能让 __iter__ 总是返回 self。否则第二次遍历时,内部状态已耗尽,直接抛 StopIteration。
解决方式是让 __iter__ 每次都新建一个干净的迭代器:
class RepeatableRange:
def __init__(self, stop):
self.stop = stop
def __iter__(self):
return RepeatableRangeIterator(self.stop) # ✅ 新实例
class RepeatableRangeIterator:
def init(self, stop):
self.stop = stop
self.i = 0
def next(self):
if self.i >= self.stop:
raise StopIteration
val = self.i
self.i += 1
return val
def iter(self):
return self
注意:RepeatableRangeIterator 自身也要实现 __iter__(返回自身),否则 iter(it) 会失败。
用生成器函数替代手动实现更安全
手动维护 __iter__ + __next__ 容易出状态错乱、漏抛 StopIteration、忘记重置等问题。Python 提供了更简洁可靠的替代:在 __iter__ 中返回一个生成器。
例如等价于上面 GoodCounter 的写法:
class GeneratorBasedCounter:
def __init__(self, n):
self.n = n
def __iter__(self):
i = 0
while i < self.n:
yield i
i += 1这种写法天然支持多次迭代(每次调用 __iter__ 都启一个新的生成器)、自动处理 StopIteration、无需手动管理状态变量生命周期。唯一要注意的是:生成器函数不能被 next() 直接调用(得先 iter()),但这本来就是迭代器协议的要求。
真正需要手写协议的场景其实很少——多数时候是封装底层流、资源或需要精细控制暂停/恢复逻辑时才值得深入。其他情况,生成器函数或 yield 就够用了。










