“List[Child]”与“List[Parent]”不兼容

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

假设我有一个

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]"

如何为此创建类型?

附注有一种方法可以使用

Sequence
类型来修复此特定错误:

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

但这对于其他类似情况没有帮助。我需要一个

List
,而不是
Sequence

python python-typing mypy
1个回答
24
投票

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

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
被有意设计为不变类型。也就是说,尽管
Child
Parent
的子类,但
List[Child]
并不是
List[Parent]
的子类型,反之亦然。您可以在此处此处找到有关不变性的更多信息。

最常见的替代方案是使用

Sequence
来代替,这是一个只读接口/协议/任何东西。由于 Sequence 是只读的,所以它是 covariant 是安全的:也就是说,
Sequence[Child]
被认为是
Sequence[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

根据您具体在做什么,您可以创建一个自定义协议,它定义一个只写列表式集合(或自定义数据结构)。由于您的数据结构是只写的,因此您可以将其设为逆变——也就是说,

WriteOnlyThing[Parent]
将是
WriteOnlyThing[Child]
的子类型。然后,您使
func
接受
WriteOnlyThing[Child]
并可以安全地传递
WriteOnlyThing[Child]
WriteOnlyThing[Parent]
的实例。

如果这两种方法都不适用于您的情况,您唯一的办法是使用

# type: ignore
来消除错误(不推荐),放弃对列表内容进行类型检查并将参数设置为
List[Any]
类型(也可以不推荐),或者弄清楚如何重组代码以使其类型安全。

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