【问题标题】:Why do I get a warning when concatenating lists of mixed types in Pycharm?为什么在 Pycharm 中连接混合类型列表时会收到警告?
【发布时间】:2019-11-06 08:58:01
【问题描述】:

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

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

【问题讨论】:

  • 对相同类型的对象使用列表是很常见的。混合内容不是错误,但我认为它的风格不好。
  • @Matthias 为什么风格不好?假设我想要一个objects(或某些超类型)列表并且需要分段构建它(子类型列表)——有没有首选的方法?
  • 如果将list1list2 注释为List[object] 类型会怎样?这使得你的程序至少在 mypy 下进行类型检查——也许它也会满足 Pycharm。
  • @Michael0x2a 这确实消除了警告,但它没有回答“为什么”......例如,如果 list1list2 本身没有类型提示,但是被分配了带有提示返回类型的函数的输出(例如def gli() -> List[int]: return [1, 2, 3] 然后list1 = gli()),我仍然收到警告。如果这些函数是库代码怎么办?为什么连接它们的输出会不好?
  • 我认为这就是 list 类型签名的定义方式——例如请参阅 typeshed 中的 signature for list.__add__,这是 stdlib 的类型提示的规范集合。类型签名可能会被扩展(例如重载以接受List[S] 并返回List[Union[T, S]])但我怀疑这种东西在实践中并不太有用,并且对于想要严格同质列表或想要子类化它们。

标签: python python-3.x pycharm type-hinting


【解决方案1】:

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

第一个原因有些平淡: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 将不再只包含整数。

当然,实际上编写这样的子类也是不好的做法,但是类型检查器并没有假设用户在不强制执行时会遵循最佳做法:类型检查器、编译器和类似工具从根本上来说是保守的野兽。


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

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

...等等。

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

【讨论】:

    【解决方案2】:

    就像 Pycharm 所说的那样,这只是一个警告,您可以连接不同的对象或列表,但这被认为是一种不好的做法。

    【讨论】:

    • 我知道警告可能是有原因的。但是为什么这被认为是不好的做法呢?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-05
    • 1970-01-01
    • 1970-01-01
    • 2014-06-25
    • 1970-01-01
    • 2020-05-22
    • 2017-02-05
    相关资源
    最近更新 更多