为什么列表列表不是序列列表?
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 值。
文档给出了一个很好的例子来说明为什么 List
s 是不变的:
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
。
我创建了以下示例:
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 值。
文档给出了一个很好的例子来说明为什么 List
s 是不变的:
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
。