假设我有一个
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
。
在这里传递列表从根本上来说不是类型安全的。例如,如果你这样做怎么办?
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]
类型(也可以不推荐),或者弄清楚如何重组代码以使其类型安全。