当pycharm中混合类型的串联列表时,为什么我会得到警告



在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.

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

按照评论中的要求,这是类型检查器不允许此的一些原因。

第一个原因有些平淡: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不再仅包含ints,添加/注射lst2的字符串。

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


最后,值得注意的是,这些问题在本质上都无法克服。类型Checker实施者可以做几件事,例如:

  1. 修补列表的类型签名(并确保它不会破坏任何现有代码(
  2. 介绍某种方式来宣布一种方法是纯粹的 - 没有突变。基本上,将PEP 591背后的想法概括为也适用于功能。(但这需要编写一个PEP,修改打字以使用新的键入结构,进行大量仔细的设计和实施工作...(
  3. 当我们知道两个变量不是列表的子类时,也许是特殊情况。(但实际上,我们确定的次数非常有限。(

...等等。

但是所有这些事情都需要时间和精力来做:这是优先级的问题。Pycharm(和Mypy等(的问题跟踪器非常长,并且不短缺其他错误/功能请求。

在其他评论中明确解释的原因,因此我想突出显示那些无法跳过代码中的串联步骤的人而不想想的人的潜在工作。看到这个烦人的警告。

在上述情况下,没有发出警告的操作:

list1 += list2

有时我必须这样做:

[*list1, *list2]

像pycharm一样,这只是一个警告,您可以连接不同的对象或列表,但被认为是不良的做法。

最新更新