为什么 mypy 推断公共基类型而不是所有包含类型的联合?
Why does mypy infer the common base type instead of the union of all contained types?
当遍历异构序列时(包含 T1
和 T2
类型的元素),mypy 推断目标变量具有 object
类型(或共享的另一个基类型)在 T1
和 T2
之间,例如 float
如果元素是 1
和 1.2
):
xs = [1, "1"]
for x in xs:
reveal_type(x) # note: Revealed type is 'builtins.object*'
推断类型 Union[T1, T2]
不是更有意义吗?然后,如果 T1
和 T2
都有一些公共基础 class 缺少的公共属性,则循环体将被允许访问该属性,而不会刺激强制转换或 isinstance 断言。
为什么 mypy 在此处推断出单个共享基类型而不是 Union
?
选择列表元素的公共基础 class(选择连接)而不是采用元素的并集是 mypy 做出的有意设计选择。
简而言之,问题是无论您选择这两种解决方案中的哪一种,您最终都会遇到边缘情况,最终会给某些人带来不便。例如,在以下情况下推断并集会很不方便,您想要 modify 或 add 到列表,而不是只读取它:
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())
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"]
因此,考虑到这两个因素,实施一些奇特的启发式方法或完全转向推断联合(并破坏大量现有代码)似乎并不值得。
当遍历异构序列时(包含 T1
和 T2
类型的元素),mypy 推断目标变量具有 object
类型(或共享的另一个基类型)在 T1
和 T2
之间,例如 float
如果元素是 1
和 1.2
):
xs = [1, "1"]
for x in xs:
reveal_type(x) # note: Revealed type is 'builtins.object*'
推断类型 Union[T1, T2]
不是更有意义吗?然后,如果 T1
和 T2
都有一些公共基础 class 缺少的公共属性,则循环体将被允许访问该属性,而不会刺激强制转换或 isinstance 断言。
为什么 mypy 在此处推断出单个共享基类型而不是 Union
?
选择列表元素的公共基础 class(选择连接)而不是采用元素的并集是 mypy 做出的有意设计选择。
简而言之,问题是无论您选择这两种解决方案中的哪一种,您最终都会遇到边缘情况,最终会给某些人带来不便。例如,在以下情况下推断并集会很不方便,您想要 modify 或 add 到列表,而不是只读取它:
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())
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"]
因此,考虑到这两个因素,实施一些奇特的启发式方法或完全转向推断联合(并破坏大量现有代码)似乎并不值得。