Jos*_*osh 4 python types type-inference python-3.x mypy
当迭代异构序列(例如,包含T1和类型的元素T2)时,mypy 推断目标变量具有类型(或和object之间共享的另一个基本类型,例如,如果元素是和):T1T2float11.2
xs = [1, "1"]
for x in xs:
reveal_type(x) # note: Revealed type is 'builtins.object*'
Run Code Online (Sandbox Code Playgroud)
推断出的类型不是更有意义吗Union[T1, T2]?然后,如果 和 都T1具有T2公共基类所缺少的一些公共属性,则将允许循环体访问该属性,而不会产生令人讨厌的强制转换或 isinstance 断言。
为什么 mypy 推断出单个共享基类型而不是此处Union?
选择列表元素的公共基类(选择连接)而不是采用元素的并集是 mypy 精心设计的选择。
简而言之,问题在于,无论您选择两种解决方案中的哪一种,最终都会遇到一些边缘情况,从而给某些人带来不便。例如,在您想要修改或添加到列表而不是仅读取列表的情况下,推断并集会很不方便:
class Parent: pass
class Child1(Parent): pass
class Child2(Parent): pass
class Child3(Parent): pass
# If foo is inferred to be type List[Union[Child1, Child2]] instead of List[Parent]
foo = [Child1(), Child2()]
# ...then this will fail with a type error, which is annoying.
foo.append(Child3())
Run Code Online (Sandbox Code Playgroud)
mypy 可能会尝试应用一些聪明的启发式方法来确定是否应该推断连接或并集,但这可能最终会导致最终用户相当混乱且难以预测。
这在实践中也是一个很容易解决的问题——例如,您可以向变量添加显式注释:
from typing import Union, Sized, List
# If you want the union
xs: List[Union[int, str]] = [1, "1"]
# If you want any object with the `__len__` method
ys: List[Sized] = [1, "1"]
Run Code Online (Sandbox Code Playgroud)
因此,考虑到这两个因素,实施一些奇特的启发式方法或完全转向推断联合(并破坏大量现有代码)似乎并不值得。