为什么 mypy 推断公共基类型而不是所有包含类型的联合? [英] Why does mypy infer the common base type instead of the union of all contained types?

查看:61
本文介绍了为什么 mypy 推断公共基类型而不是所有包含类型的联合?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当迭代异构序列(包含 T1T2 类型的元素,例如)时,mypy 推断目标变量具有类型 object (或在 T1T2 之间共享的另一种基本类型,例如 float 如果元素是 1>1.2):

xs = [1, "1"]对于 x 中的 x:Reveal_type(x) # 注意:显示的类型是'builtins.object*'

推断类型为 Union[T1, T2] 不是更有意义吗?然后,如果 T1T2 都有一些公共基类缺少的公共属性,循环体将被允许访问该属性,而不会刺激强制转换或 isinstance 断言.>

为什么 mypy 在这里推断单个共享基类型而不是 Union?

解决方案

选择列表元素的公共基类(选择连接)而不是采用元素的并集是 mypy 做出的深思熟虑的设计选择.

>

简而言之,问题在于无论您选择两种解决方案中的哪一种,最终都会遇到对某些人来说不方便的边缘情况.例如,在您想要修改添加到列表而不是仅读取列表的情况下,推断联合会很不方便:

class 父级:pass类 Child1(Parent):通过类 Child2(Parent):通过类 Child3(Parent):通过# 如果 foo 被推断为类型 List[Union[Child1, Child2]] 而不是 List[Parent]foo = [Child1(), Child2()]# ...那么这将失败并出现类型错误,这很烦人.foo.append(Child3())

mypy 可能会尝试应用一些巧妙的启发式方法来确定它应该推断联接还是联合,但这最终可能会导致最终用户相当混乱且难以预测.

这在实践中也是一个非常容易解决的问题——例如,您可以向变量添加显式注释:

from 输入 import Union, Sized, List# 如果你想要联合xs: List[Union[int, str]] = [1, "1"]# 如果你想要任何带有 `__len__` 方法的对象ys: 列表[大小] = [1, "1"]

因此,考虑到这两个因素,实施一些奇特的启发式方法或切换到完全推断联合(并破坏大量现有代码)似乎并不值得.

When iterating over a heterogeneous sequence (containing elements of type T1 and T2, say), mypy infers the target variable to have type object (or another base type shared between T1 and T2, e.g. float if the elements were 1 and 1.2):

xs = [1, "1"]
for x in xs:
    reveal_type(x)  # note: Revealed type is 'builtins.object*'

Wouldn't it make more sense for the inferred type to be Union[T1, T2]? Then if both T1 and T2 have some common attribute which the common base class lacks, the loop body would be allowed to access that attribute without irritating casts or isinstance assertions.

Why does mypy infer a single shared base type instead of a Union here?

解决方案

Picking the common base class of the list elements (picking the join) instead of taking the union of the elements is a deliberate design choice that mypy made.

In short, the problem is that no matter which of the two solutions you pick, you'll always end up with edge cases that end up being inconvenient for somebody. For example, inferring the union would be inconvenient in cases like the following where you want to modify or add to the list, instead of only reading it:

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())

It's possible that mypy could perhaps try applying some clever heuristic to determine whether it should infer a join or a union, but that'll probably end up being fairly confusing and difficult to predict for end-users.

This is also a pretty easy issue to work around in practice -- for example, you could just add an explicit annotation to your variable:

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"]

So given these two factors, implementing some fancy heuristic or switching to inferring unions entirely (and disrupting a lot of existing code) doesn't really seem worth it.

这篇关于为什么 mypy 推断公共基类型而不是所有包含类型的联合?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆