在Pycharm中串联混合类型的列表时,为什么会收到警告?

时间:2019-06-24 14:14:55

标签: python python-3.x pycharm typehints

在Pycharm中,以下代码会产生警告:

from typing import List

list1: List[int] = [1, 2, 3]
list2: List[str] = ["1", "2", "3"]
list3: List[object] = list1 + list2
#                             ↳ Expected type List[int] (matched generic type List[_T]),
#                               got List[str] instead.

为什么?我不应该将两个混合的,提示类型的列表串联起来吗?

2 个答案:

答案 0 :(得分:3)

根据评论的要求,这是类型检查器不允许这样做的一些原因。

第一个原因有些平淡:list.__add__的类型签名除了包含相同类型的列表之外根本不允许任何其他输入:

_T = TypeVar('_T')

# ...snip...

class list(MutableSequence[_T], Generic[_T]):

    # ...snip...

    def __add__(self, x: List[_T]) -> List[_T]: ...

支持PEP 484的Pycharm使用(部分)来自Typeshed的数据。

我们有可能以某种方式扩展此类型签名(例如,重载它以同时接受List[_S]并返回List[Union[_T, _S]]),但我认为没有人愿意调查这种方法的可行性:这种事情在实践中不太有用,对于想要严格同质的列表或想要对其进行子类化的人来说,生活变得更加艰难,并可能破坏现有代码的 lot 依靠当前的类型签名。

此类型签名也可能反映了PEP 484最初设计期间所做的更广泛的选择,即假定列表始终是同质的-始终包含相同类型的值。

严格来说,PEP 484的设计人员无需做出选择:他们可能需要类型检查器来进行特殊情况的交互,就像我们当前对元组所做的那样。我认为,总的来说,不这样做很简单。 (而且可以说是更好的样式,但无论如何。)


第二个原因与PEP 484类型系统的基本限制有关:无法声明某些函数或方法不会修改状态。

基本上,只有保证lst1.__add__(lst2)不会突变两个操作数时,您想要的行为才是安全的。但是实际上无法保证这一点-如果lst1是一些奇怪的列表子类,该子类将lst2中的项目复制到自身呢?然后暂时将lst1的类型从SomeListSubtype[int]放到SomeListSubtype[object]是不安全的:lst1在从lst2添加/注入字符串后不再仅包含整数。

当然,实际上编写这样的子类也是不好的做法,但是类型检查器没有奢望用户如果不执行它们就会遵循最佳实践:类型检查器,编译器和类似工具从根本上是保守的野兽。


最后,值得注意的是,这些问题都不是本质上无法克服的。类型检查器实现者可以执行几种操作,例如:

  1. 修补list的类型签名(并确保它不会破坏任何现有代码)
  2. 引入某种方法来声明一种方法是纯的-不做任何突变。基本上,概括一下PEP 591后面的观点,以也适用于函数。 (但这需要编写PEP,修改排版以使用新的打字构造,并进行大量精心的设计和实现工作...)
  3. 当我们确定两个变量不是列表的子类时,这种交互可能是特殊情况。 (但实际上,我们可以肯定地知道这一点的次数是非常有限的。)

...等等。

但是所有这些事情都需要时间和精力来做:这是优先事项。 Pycharm(和mypy等)的问题跟踪器很长,并且不乏其他需要解决的错误/功能请求。

答案 1 :(得分:0)

就像Pycharm所说,这只是警告,允许您连接不同的对象或列表,但这被认为是不好的做法。