为何直接迭代自定义Iterable对象类型正常,zip中却为Any?
问题:为何直接迭代与zip中迭代的类型推断结果不同?
测试脚本
from collections.abc import Iterable, Iterator class A(Iterable): _list: list[int] def __init__(self, *args: int) -> None: self._list = list(args) def __iter__(self) -> Iterator[int]: return iter(self._list) a = A(1, 2, 3) for i in a: reveal_type(i) for s, j in zip("abc", a): reveal_type(j)
mypy运行输出
$ mypy test.py test.py:17: note: Revealed type is "builtins.int" test.py:20: note: Revealed type is "Any" Success: no issues found in 1 source file
问题
为何直接迭代对象a时类型为int,而在zip中迭代时j的类型却为Any?
注:子类化class A(Iterable[int])可实现正确类型解析,但这并非本次问题的关注点;)
原因解析
这是因为mypy在两种场景下的类型推断逻辑存在差异:
- 直接迭代场景:当在
for循环中直接迭代a时,mypy会直接解析__iter__方法的返回类型。由于你的__iter__明确标注返回Iterator[int],mypy可以直接确定迭代元素的类型是int。 - zip调用场景:
zip函数的类型推断依赖于传入参数的泛型类型声明。你的类A继承的是未指定类型参数的Iterable,这在类型检查中等价于Iterable[Any]。当zip处理a时,它会使用Iterable的默认泛型参数Any,而不会从__iter__方法的返回类型逆向推导父类的泛型参数——mypy的类型推断规则不会为了适配zip的调用,去动态补全父类的泛型信息。
简单来说,直接迭代时mypy关注的是__iter__的具体返回类型,而zip则依赖类声明时的泛型标注,两者的类型推断路径不同,导致了结果差异。
内容的提问来源于stack exchange,提问作者hintze
相关产品推荐
相关产品推荐

