因为含 yield 的 iter 是生成器函数,调用后自动返回满足迭代器协议的生成器对象,无需手动实现 next 或返回 self。

为什么直接在 __iter__ 里写 yield 就能工作?
因为 Python 规定:只要一个方法包含 yield,它就是生成器函数,调用后返回一个生成器对象——而生成器对象天然实现了 __iter__ 和 __next__,满足迭代器协议。所以你不需要额外写 __next__,也不需要手动构造 Iterator 子类。
常见错误是试图在 __iter__ 中返回 self 同时又用 yield,这会导致“TypeError: iter() returned non-iterator”,因为返回的是生成器(正确),但你又误以为要自己管理状态。
-
__iter__必须返回一个迭代器对象(可以是生成器、自定义类实例、或任何带__next__的对象) - 用
yield的__iter__自动返回生成器,符合要求 - 不要在该方法末尾加
return self或return None—— 这会覆盖掉生成器返回值
如何让生成器 __iter__ 支持多次迭代?
默认情况下,生成器只能遍历一次(第二次调用 iter(obj) 得到的新生成器从头开始,但内部状态不共享)。如果你希望每次 for 都获得独立、可重入的迭代,__iter__ 必须每次返回一个新生成器。
典型陷阱是把状态存在实例变量里,然后在 yield 中直接读取并修改——这样多个迭代器会互相干扰。正确做法是把数据源和逻辑封装在生成器内部,不依赖外部可变状态。
SkillSub Pro - Python 题解与代码注释双功能技能功能概述SkillSub Pro - Python 题解与代码注释双功能技能是一项面向实际任务的技能,主要用于SkillSub Pro 是一个 Python 题解生成与代码注释的 双功能合体技能 ,专为学生、算法学习者和开发者设计;✅ 一个技能,两种用途 :;核心要点📝 题解模式 :输入题目/题号,自动生成完整 Python 题解(含详细注释、解题思路、复杂度分析);💬 注释模式 :输入 Python 代码,自动添加详细中。它将相关步骤、
- ✅ 正确:在
__iter__函数体内直接yield基于self.data的副本或只读遍历 - ❌ 错误:用
self.index += 1管理位置,导致第二次for从中间开始 - 如果需支持索引/切片,建议用
itertools.islice或返回iter(self.data)而非手写计数逻辑
def __iter__(self):
# 安全:每次调用都生成全新迭代器
for item in self._data:
yield item * 2
什么时候不该用生成器实现 __iter__?
当你要暴露底层可变状态、需要暂停/恢复控制权、或必须兼容老版本 Python(如 2.7)时,生成器方案可能不合适。更关键的是:如果你的类本身就要被子类继承并重写迭代行为,用生成器会让子类难以扩展——因为它把逻辑锁死在函数体里,无法像普通方法那样被 super() 调用。
- 需要延迟计算且逻辑简单 → 用
yield最简洁 - 需要与上下文管理器配合(如文件流)、或需在
__next__中做异常处理 → 写独立迭代器类更可控 - 类设计为基类,预期被继承 → 显式定义
__next__并分离迭代逻辑,避免子类无法复用 - PyPy 或某些嵌入式 Python 环境对生成器开销敏感 → 测量实际性能再决定
生成器 __iter__ 和 iter() 的兼容性注意点
绝大多数场景下没问题,但要注意两个边界情况:一是 collections.abc.Iterator 类型检查会失败(生成器是 Generator,不是 Iterator 的直接实例);二是某些框架(如旧版 numpy 或 pandas 的某些接口)可能显式检查 hasattr(obj, '__next__'),而生成器对象确实有,但它的 __next__ 是绑定到生成器实例的,不是类方法。
- 类型提示可用
Iterator[T],但运行时isinstance(obj, Iterator)返回False - 若需通过
Iterator检查,得手动继承collections.abc.Iterator并实现__next__ -
list(obj)、sum(obj)等内置函数完全兼容生成器返回值,无需担心
真正容易被忽略的是调试时打印 type(obj) —— 你会看到 <class></class>,而不是你的类名,这有时会让日志或断点追踪变得不直观。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










