假定我有一个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
。
答案 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
被故意设计为不变类型。也就是说,即使Child
是Parent
的子类,也不是List[Child]
是List[Parent]
的子类型,反之亦然。您可以找到有关不变性here和here的更多信息。
最常见的替代方法是改用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}}(也不建议使用),或者弄清楚如何重组代码以确保其类型安全。