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

2024-03-29 14:36:46 发布

您现在位置:Python中文网/ 问答频道 /正文

我创建了以下示例:

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]时可以返回List[Circle],而当它期望List[Sequence[Circle]]时不能返回List[List[Circle]]?你知道吗

更具体地说,当值是返回值时,为什么这不正常?我想我理解为什么它不能作为一个参数,但我不明白为什么这个值不能被接受为返回值。你知道吗

这些文档给出了一个很好的例子,说明了List是不变的原因:

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]。你知道吗

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


Tags: 函数returnvaluemydefpasslistclass
1条回答
网友
1楼 · 发布于 2024-03-29 14:36:46

再一次,在打这个问题的时候,我想我已经找到了答案。你知道吗

考虑以下情况:

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必须跟踪变量是何时声明的,并确保在允许我们将返回值用作返回值之前,不依赖于在函数返回之后将返回值视为List[List[Circle]]。你知道吗

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

底线是Mypy不做这种分析。所以,为了让Mypy知道这是好的,我们应该投它。你知道吗

换句话说,我们知道返回值永远不会再用作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。你知道吗

相关问题 更多 >