Python list[T] 不可分配给 list[T | 没有任何]

zmb*_*mbq 1 python python-typing pylance

我有一个类型Tnamedtuple实际上是:

from collections import namedtuple

T = namedtuple('T', ('a', 'b'))
Run Code Online (Sandbox Code Playgroud)

list[T | None]我有一个接受 a和列表的函数:

def func(arg: list[T | None]):
    ...


l = [T(1, 2), T(2, 3)]
func(l)  # Pylance error
Run Code Online (Sandbox Code Playgroud)

l属于 类型list[T]

当我将 a 传递l给函数时,我从 Pylance 收到错误,指出 a 与list[T]不兼容,list[T | None]因为T cannot be assigned to T | None.

除了手动指定 mylist[T]实际上是 a之外list[T | None],我还能做些什么来使其正常工作而不出错?当然,在运行时一切都会按预期运行。

Dan*_*erg 6

内置的可变泛型集合类型(例如)的类型参数list都是不变的。(参见PEP 484

这意味着给定的类型S是 的子类型Tlist[S]不是子类型(也不是超类型)list[T]

您可以进一步简化您的错误:

def f(a: list[int | None]) -> None: ...

b = [1, 2]
f(b)
Run Code Online (Sandbox Code Playgroud)

int显然是 的子类型int | None,但list[int]不是 的子类型list[int | None]

对于上面的代码,Mypy 非常友好地提供了额外的信息,准确地告诉我们:

error: Argument 1 to "f" has incompatible type "List[int]"; expected "List[Optional[int]]"  [arg-type]
note: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
note: Consider using "Sequence" instead, which is covariant
Run Code Online (Sandbox Code Playgroud)

除了您之前提到的显式注释列表的明显解决方案之外list[T | None],也许您可​​以遵循 Mypy 的建议,并将函数接受的类型更改为不太具体的类型,该类型仍然提供您需要的协议,但也恰好是协变的在其类型参数中,例如collections.abc.Sequence

error: Argument 1 to "f" has incompatible type "List[int]"; expected "List[Optional[int]]"  [arg-type]
note: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
note: Consider using "Sequence" instead, which is covariant
Run Code Online (Sandbox Code Playgroud)

这应该会顺利通过。


您没有提到您的函数到底对该列表做了什么,但是如果您打算在函数内部对其进行变异Sequence,那么具有不可变类型的解决方案显然将不起作用。

可变性确实这里的问题,也是为什么list首先声明不变的原因。Mypy 文档对此推理提供了非常好的解释。

因此,虽然在技术上可以使用协变类型变量来定义您自己的通用协议,该变量模拟您在该函数中所需的方法,但我不确定这是否是一个好主意。最好重新考虑该函数实际需要什么以及调用它什么是安全的。