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

时间:2018-11-13 06:31:47

标签: 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)

对此运行mypy会产生错误:

 error: Argument 1 to "func" has incompatible type "List[Child]"; expected "List[Parent]"

如何为此创建类型?

P.S。有一种方法可以使用Sequence类型来解决此特定错误:

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

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

1 个答案:

答案 0 :(得分:1)

在这里传递列表从根本上讲不是类型安全的。例如,如果您执行此操作怎么办?

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()!

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

要使用类型术语,List被故意设计为不变类型。也就是说,即使ChildParent的子类,也不是List[Child]List[Parent]的子类型,反之亦然。您可以找到有关不变性herehere的更多信息。

最常见的替代方法是改用Sequence,这是只读接口/协议/任何内容。而且,由于Sequence是只读的,因此它是协变变量是安全的:也就是说,Sequence[Child]被认为是Sequence[Parent]的有效子类型。

根据您要执行的操作,也许可以使用type variables来代替。例如。而不是说“此函数接收父级列表”,而是说“此函数接收父级或父级子类的任何类的列表”:

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

根据您的实际工作,可以创建一个custom Protocol来定义类似写列表的集合(或自定义数据结构)。而且,由于您的数据结构是只写的,因此可以将其设为 contravariant ,即WriteOnlyThing[Parent]将是WriteOnlyThing[Child]的子类型。然后,您使func接受WriteOnlyThing[Child],并且可以安全地传递WriteOnlyThing[Child]WriteOnlyThing[Parent]的实例。

如果两种方法都不适合您,则唯一的办法是使用# type: ignore使错误消失(不建议),放弃对列表内容的类型检查,并使用类型{ {1}}(也不建议使用),或者弄清楚如何重组代码以确保其类型安全。