为什么列表列表不是序列列表?

Why is a list of lists not a list of sequences?

我创建了以下示例:

from typing import List, Sequence

class Circle:
    pass

def foo(circle: Circle) -> Sequence[Circle]:
    return_value: List[Circle] = [circle]
    return return_value

def bar(circle: Circle) -> List[Sequence[Circle]]:
    # Incompatible return value type (got "List[List[Circle]]", expected "List[Sequence[Circle]]")
    return_value: List[List[Circle]] = [[circle]]
    return return_value

为什么在期望 Sequence[Circle] 时 return 可以 List[Circle],而在期望 List[Sequence[Circle]]List[List[Circle]] 却不行?

更具体地说,当值是 return 值时,为什么这不行?我想我明白为什么它不能作为参数,但我不明白为什么这个值不被接受为 return 值。

文档给出了一个很好的例子来说明为什么 Lists 是不变的:

class Shape:
    pass

class Circle(Shape):
    def rotate(self):
        ...

def add_one(things: List[Shape]) -> None:
    things.append(Shape())

my_things: List[Circle] = []
add_one(my_things)     # This may appear safe, but...
my_things[0].rotate()  # ...this will fail

这里的想法是,如果你把你的 List[Subclass] 传递给认为它是 List[Superclass] 的东西,该函数可以编辑你的 List[Subclass] 以便它包含 Superclass个元素,所以在函数运行.

之后变成了一个List[Superclass]

但是,作为一个 return 值,我不明白为什么这是一个问题。一旦它退出那个函数,每个人都会把它当作一个List[Sequence[Circle]],就是这样,所以应该没有问题。

再一次,在输入这​​个问题时,我想我已经找到了答案。

考虑以下情况:

from typing import List, Sequence

class Circle:
    pass

def baz(circle_list_matrix: List[List[Circle]]) -> List[Sequence[Circle]]:
    # Incompatible return value type (got "List[List[Circle]]", expected "List[Sequence[Circle]]")
    return circle_list_matrix

在这里,Mypy 引发错误是绝对正确的,因为使用 circle_list_matrix 的其他函数可能依赖于它是 List[List[Circle]],但之后的其他函数可能会将其修改为List[Sequence[Circle]].

为了确定我们处于哪种情况,Mypy 必须跟踪声明变量的时间,并确保没有任何事情依赖于将 return 值视为 List[List[Circle]] 在函数 returns 之后(即使它是这样键入的)在允许我们将其用作 return 值之前。

(请注意,在函数 returns 之前将其视为 List[List[Circle]] 应该不是坏事,因为在这些点上它是 List[List[Circle]]。另外,如果它总是被当作 List[Sequence[Circle]],然后我们就可以毫无问题地输入它。当某些东西将它当作 List[List[Circle]] 时,问题就出现了,例如 circle_list_matrix[0].append(Circle()),所以我们必须将其键入 List[List[Circle]] 才能执行该操作,但在函数 returns 之后每次都会将其视为 List[Sequence[Circle]]。)

归根结底,Mypy 不会进行此类分析。所以,为了让 Mypy 知道这是可以的,我们应该直接转换它。

换句话说,我们知道return值永远不会再被用作List[List[Circle]],所以baz应该写成:

def baz(circle_list_matrix: List[List[Circle]]) -> List[Sequence[Circle]]:
    # works fine
    return cast(List[Sequence[Circle]], circle_list_matrix)

其中 cast 是从 typing 导入的。

相同的转换技术可以应用于问题代码中的 bar