Python 类型中的协变和不变集合

问题描述 投票:0回答:2

我在 python 中遇到的一个问题是 List 类型是不变的 - 这意味着它只能保存特定类型的对象,否则你会得到类型错误(例如运行 mypy 时) - 但有时你需要在更通用的函数中使用集合,该函数可以接受基类的所有类型(让我们称之为

A
)以及所有派生类。一个明显的例子是这个

class A:
    pass
   
class B(A):
    pass

def print_list_a(my_list: list[A]) -> None:
    print(my_list)

l1 = [A()]
l2 = [B()]

# print_list_a(l1)
# print_list_a(l2) 

当我在严格模式下使用 mypy 运行上述内容时,我得到以下内容

main.py:41: error: Argument 1 to "print_list_a" has incompatible type "list[B]"; expected "list[A]"  [arg-type]
main.py:41: note: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
main.py:41: note: Consider using "Sequence" instead, which is covariant

处理这个问题的最佳方法是什么?使用协变而不是不变集合(即序列而不是列表)有什么好处?

python mypy python-typing
2个回答
0
投票

我遇到了这个问题的两种解决方案。

一种是定义一个绑定到 A 的泛型类型,另一种是使用协变类型(如 Sequence),像这样

T = TypeVar('T', bound=A)

def print_list_t(my_list: List[T]) -> None:
    print(my_list)


# This is fine!
print_list_t(l1)
print_list_t(l2)


# use covariant type Sequence in place of list
def print_sequence_a(my_sequence: Sequence[A]) -> None:
    print(my_sequence)

# This is fine too because the sequence type is covariant
print_sequence_a(l1)
print_sequence_a(l2)

使用绑定类型的优点是类型

List
比Sequence更具体。但是,如果您很高兴您的函数可以接受任何类型的序列,那么使用这种协变类型会更容易。


0
投票

您可以简单地将

l2
注释为
list[A]
:

class A:
    pass

class B(A):
    pass

def print_list_a(my_list: list[A]) -> None:
    print(my_list)

l1: list[A] = [A()]
l2: list[A] = [B()]

print_list_a(l1)
print_list_a(l2)

此类型在 PyRight 中检查。

© www.soinside.com 2019 - 2024. All rights reserved.