在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.
为什么?我不应该连接两个混合的,提示类型的列表吗?
3条答案
按热度按时间eivgtgni1#
正如评论中所要求的,下面是类型检查器不允许这样做的一些原因。
第一个原因有点平淡无奇:
list.__add__
的类型签名只允许传入包含相同类型的列表以外的任何内容:而支持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/特性请求的短缺。
xqkwcwgp2#
原因在其他注解中有明确的解释,所以我只想为那些不能跳过代码中的连接步骤,也不想看到这个恼人的警告的人强调潜在的变通办法。
在上述情况下,未产生警告的操作:
list1 += list2
而有时我却不得不这样做:
[*list1, *list2]
个r7xajy2e3#
就像Pycharm说的,这只是一个警告,你可以连接不同的对象或列表,但这被认为是一个坏的做法。