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

9njqaruj  于 2022-11-08  发布在  PyCharm
关注(0)|答案(3)|浏览(172)

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

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

eivgtgni

eivgtgni1#

正如评论中所要求的,下面是类型检查器不允许这样做的一些原因。
第一个原因有点平淡无奇: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]]),但我认为没有人会费心研究此方法的可行性:这类东西在实践中并不太有用,对于那些想要严格同构列表或想要将它们子类化的人来说,这会使他们的工作更加困难,并且可能会破坏 * 大量 * 依赖于当前类型签名的现有代码。
这种类型签名也可能反映了在PEP 484的初始设计期间所做的更广泛的选择,即假设列表总是同质的--总是包含相同类型的值。
严格来说,PEP 484的设计者并不需要做出这样的选择:他们可能需要类型检查器来与它进行特殊情况的交互,就像我们目前对元组所做的那样。但我认为,不这样做总体上更简单。(而且可以说样式更好,但不管怎样。)
第二个原因与PEP 484型系统的基本限制有关:没有办法声明某个函数或方法不修改状态。
基本上,只有当lst1.__add__(lst2)保证不改变任何一个操作数时,你想要的行为才是安全的。但是没有办法真正保证这一点--如果lst1是某个奇怪的列表子类,它把lst2中的项复制到它自己呢?那么暂时把lst1的类型从SomeListSubtype[int]放宽到SomeListSubtype[object]将是不安全的:在添加/注入来自lst2的字符串之后,lst1将不再仅包含int。
当然,实际上编写这样的子类也是不好的做法,但是类型检查器并没有奢侈地假设用户会遵循最佳做法,如果它们没有被强制执行的话:类型检查器、编译器和类似的工具基本上都是保守的野兽。
最后,值得注意的是,这些问题本质上都不是不可克服的。类型检查器实现者可以做一些事情,例如:
1.修改list的类型签名(并确保它不会破坏任何现有代码)
1.引入某种方式来声明一个方法是纯的--不做任何改变。基本上,将PEP 591背后的思想推广到也适用于函数。(但这需要编写PEP,修改typeshed以使用新的类型化构造,做大量仔细的设计和实现工作...)
1.当我们确定这两个变量不是列表的子类时,这种交互作用可能是特例(但实际上,我们确定这种交互作用的次数是非常有限的)。
...等等。
但所有这些事情都需要时间和精力去做:PyCharm(和mypy等)的问题跟踪器相当长,而且没有其他bug/特性请求的短缺。

xqkwcwgp

xqkwcwgp2#

原因在其他注解中有明确的解释,所以我只想为那些不能跳过代码中的连接步骤,也不想看到这个恼人的警告的人强调潜在的变通办法。
在上述情况下,未产生警告的操作:
list1 += list2
而有时我却不得不这样做:
[*list1, *list2]

r7xajy2e

r7xajy2e3#

就像Pycharm说的,这只是一个警告,你可以连接不同的对象或列表,但这被认为是一个坏的做法。

相关问题