为何实现__getitem__的类无法通过collections.abc.Iterable检测?
根据PEP 0544,collections.abc模块的抽象类通过自定义__subclasshook__方法支持运行时结构检查,实现__iter__的类可通过isinstance检测为Iterable。但Python术语表指出,Iterable可实现__iter__或符合序列语义的__getitem__,然而实现__getitem__的类无法通过isinstance检测,且Mypy也不将其识别为Iterable,这一现象的原因是什么?
代码示例
1. 可通过检测的实现
from collections.abc import Iterable class MyIterable: def __iter__(self): return [] assert isinstance(MyIterable(), Iterable)
2. 无法通过检测的实现
from collections.abc import Iterable class MyIterable: def __getitem__(self, item): return [] assert isinstance(MyIterable(), Iterable)
报错信息:
Traceback (most recent call last): File "file.py", line 7, in <module> assert isinstance(MyIterable(), Iterable) AssertionError
3. Mypy类型校验示例
from collections.abc import Iterable class MyIterable1: def __iter__(self): return [] class MyIterable2: def __getitem__(self): return [] def foo(bar: Iterable): ... foo(MyIterable1()) foo(MyIterable2())
Mypy结果:
$ mypy .\scratch_443.py test_file.py:15: error: Argument 1 to "foo" has incompatible type "MyIterable2"; expected "Iterable[Any]" Found 1 error in 1 file (checked 1 source file)
原因解析
1. 运行时检测:__subclasshook__的实现限制
collections.abc.Iterable的__subclasshook__方法只检查类是否实现了__iter__方法,并没有包含对__getitem__的检测逻辑。
Python术语表中提到的“实现__getitem__也属于Iterable”,是从语义层面定义的——只要类的__getitem__能接收从0开始的整数索引,就可以通过iter()函数生成迭代器,符合迭代器协议的语义。但collections.abc.Iterable的运行时检测并没有跟进这个语义定义,而是只做了最直接的方法存在性检查。
2. Mypy类型校验:静态类型系统的规则
Mypy作为静态类型检查工具,严格遵循collections.abc.Iterable的类型定义——该类型被定义为必须实现__iter__方法。虽然Python运行时允许通过__getitem__实现迭代语义,但Mypy的类型规则并没有将这种情况纳入Iterable的类型范畴,因为静态检查无法可靠判断__getitem__是否符合“从0开始的整数索引”的序列语义。
另外,示例中MyIterable2的__getitem__定义本身存在问题:它没有接收索引参数(正确签名应为def __getitem__(self, item: int)),即便修复这个签名,Mypy依然不会将其识别为Iterable,因为它的类型规则里Iterable只关联__iter__方法。
内容的提问来源于stack exchange,提问作者Dorian Turba

