mypy:创建一个接受子类实例列表的类型

kur*_*tgn 4 python mypy

假设我有一个Child类是类的子Parent类,以及一个接受Parent子类实例列表的函数:

from typing import List


class Parent:
    pass


class Child(Parent):
    pass


def func(objects: List[Parent]) -> None:
    print(objects)


children = [Child()]
func(children)
Run Code Online (Sandbox Code Playgroud)

mypy在此运行会产生错误:

 error: Argument 1 to "func" has incompatible type "List[Child]"; expected "List[Parent]"
Run Code Online (Sandbox Code Playgroud)

如何为此创建类型?

PS有一种方法可以使用Sequence类型修复此特定错误:

def func(objects: Sequence[Parent]) -> None:
    print(objects)
Run Code Online (Sandbox Code Playgroud)

但这在其他类似情况下无济于事。我需要一个List,而不是一个Sequence

Mic*_*x2a 7

在这里传递列表从根本上来说不是类型安全的。例如,如果你这样做呢?

def func(objects: List[Parent]) -> None:
    print(objects)
    objects.append(Parent())

children: List[Child] = [Child(), Child(), Child()]
func(children)
# Uh-oh! 'children' contains a Parent()!
Run Code Online (Sandbox Code Playgroud)

如果允许进行类型检查,您的代码最终将包含一个错误。

使用类型行话,List有意设计成不变类型。也就是说,即使Child是 的子类Parent,也不List[Child]是 的子类型List[Parent],反之亦然。您可以在此处此处找到有关不变性的更多信息

最常见的替代方法是使用Sequence它,它是一个只读的接口/协议/任何东西。并且由于 Sequence 是只读的,所以它是协变是安全的:也就是说,Sequence[Child]被认为是一个有效的子类型Sequence[Parent].

根据您的具体操作,您或许可以改用类型变量。例如,不是说“这个函数接受一个 Parent 的列表”,而是说“这个函数接受一个 Parent 或 Parent 子类的任何类的列表”:

TParent = TypeVar('TParent', bound=Parent)

def func(objects: List[TParent]) -> List[TParent]:
    print(objects)

    # Would not typecheck: we can't assume 'objects' will be a List[Parent]
    objects.append(Parent())  

    return objects
Run Code Online (Sandbox Code Playgroud)

根据您的具体操作,您可以 maaaaaaaaybe 创建一个自定义协议,该协议定义了一个只写类列表的集合(或自定义数据结构)。并且由于您的数据结构将是只写的,您可以使其逆变——也就是说,WriteOnlyThing[Parent]将是WriteOnlyThing[Child]. 然后,您func接受WriteOnlyThing[Child]并可以安全地传递WriteOnlyThing[Child]WriteOnlyThing[Parent]

如果这两种方法都不适用于您的情况,您唯一的办法是要么使用# type: ignore消除错误(不推荐),放弃对列表内容进行类型检查并制作类型参数List[Any](也不推荐),要么找出如何重组您的代码以使其类型安全。