__iter__ 方法必须返回迭代器对象而非可迭代对象;正确做法是返回 iter(self.items)、自定义迭代器实例或生成器函数调用结果,确保每次调用返回独立状态的新迭代器。

__iter__ 方法必须返回一个迭代器对象,不能直接返回列表或生成器表达式
很多人写 __iter__ 时直接 return self.items(假设 self.items 是 list),这会导致实例无法被多次迭代——因为 list 本身是可迭代对象,但不是迭代器。Python 的 for 循环、next() 等操作要求调用 __iter__ 后得到的对象必须有 __next__ 方法。
正确做法是返回一个具备完整迭代器协议的对象:
- 返回
iter(self.items)(最常用,复用内置逻辑) - 返回自定义类实例(该类实现
__next__和__iter__) - 返回生成器函数调用结果(如
self._gen(),其中_gen是带yield的方法)
不要在 __iter__ 里重置内部状态,除非你明确需要每次迭代都从头开始
如果类自身维护游标(比如有个 self.index),在 __iter__ 中把它重置为 0 是常见错误——这会让同一个实例无法支持多个并发迭代器(例如嵌套 for 循环或 zip(a, a))。Python 迭代器协议要求:每个调用 __iter__ 返回的迭代器应独立工作。
正确策略:
立即学习“Python免费学习笔记(深入)”;
- 让
__iter__总是返回新对象(如return MyIterator(self.items)) - 或者返回生成器函数调用(天然隔离状态):
return self._iterate(),其中_iterate是def _iterate(self): yield from self.items - 避免在
__iter__中修改self.index或类似字段
生成器函数是最简洁安全的 __iter__ 实现方式
写一个单独的生成器方法,然后在 __iter__ 中调用它,既清晰又自动满足协议——生成器对象原生支持 __iter__ 和 __next__,且每次调用都产生新状态。
示例:
class Countdown:
def __init__(self, start):
self.start = start
<pre class="brush:php;toolbar:false;">def __iter__(self):
return self._countdown() # 每次返回新生成器
def _countdown(self):
n = self.start
while n > 0:
yield n
n -= 1
注意:return self._countdown() 不是 return self._countdown(后者返回函数对象,没调用);也不能写成 yield from self._countdown()——那会让 __iter__ 自身变成生成器,违反协议(__iter__ 必须返回迭代器,不是生成器函数)。
区分“可迭代对象”和“迭代器”,别混淆 __iter__ 的返回值类型
常见误判:以为实现了 __iter__ 就是迭代器。其实不是——它是“可迭代对象”。真正的迭代器必须同时有 __iter__(通常返回 self)和 __next__。如果你的类既要当容器又要当迭代器(比如单次遍历的流式处理器),那它得自己实现 __next__,并在 __iter__ 中返回 self。
但要注意副作用:
- 这种设计下,同一个实例只能被迭代一次(第二次会立即抛
StopIteration) - 无法与
itertools中多数函数兼容(它们假定输入是可重复迭代的) - 调试时容易困惑:为什么
list(obj); list(obj)第二次是空的?
除非业务场景明确要求单次消费(如读取一次的文件流、网络响应体),否则优先用“可迭代对象 + 独立迭代器”的分离模式。
最容易被忽略的是:__iter__ 的语义不是“开始迭代”,而是“提供一个能迭代的东西”。它的返回值生命周期、状态隔离、是否可重入,直接决定你的类在真实数据流场景中会不会出 bug。


















