mypy:创建一个接受子类实例列表的类型
mypy: creating a type that accepts list of instances of subclasses
假设我有一个 Child
class,它是 Parent
class 的子 class,以及一个接受实例列表的函数Parent
子classes:
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
。
从根本上说,在此处传递列表不是类型安全的。例如,如果你这样做呢?
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 and here.
最常见的替代方法是使用 Sequence
,这是只读的 interface/protocol/whatever。由于 Sequence 是只读的,因此它是 协变的 是安全的:也就是说,Sequence[Child]
被认为是 Sequence[Parent]
.[= 的有效子类型31=]
根据您的具体操作,您也许可以使用 type variables。例如。而不是说 "this function takes in a list of Parent",你说 "this function takes in a list of any class which is Parent, or a subclass of 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
根据您的具体操作,您可以创建一个 custom Protocol 来定义一个只写列表类集合(或自定义数据结构)。由于您的数据结构是只写的,您可以使它成为 逆变——也就是说,WriteOnlyThing[Parent]
将是 WriteOnlyThing[Child]
的子类型。然后让 func
接受 WriteOnlyThing[Child]
并且可以安全地传入 WriteOnlyThing[Child]
和 WriteOnlyThing[Parent]
的实例。
如果这两种方法都不适用于您的情况,您唯一的办法是使用 # type: ignore
来消除错误(不推荐),放弃对列表内容进行类型检查并使参数为输入 List[Any]
(同样不推荐),或弄清楚如何重构代码以使其类型安全。
假设我有一个 Child
class,它是 Parent
class 的子 class,以及一个接受实例列表的函数Parent
子classes:
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
。
从根本上说,在此处传递列表不是类型安全的。例如,如果你这样做呢?
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 and here.
最常见的替代方法是使用 Sequence
,这是只读的 interface/protocol/whatever。由于 Sequence 是只读的,因此它是 协变的 是安全的:也就是说,Sequence[Child]
被认为是 Sequence[Parent]
.[= 的有效子类型31=]
根据您的具体操作,您也许可以使用 type variables。例如。而不是说 "this function takes in a list of Parent",你说 "this function takes in a list of any class which is Parent, or a subclass of 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
根据您的具体操作,您可以创建一个 custom Protocol 来定义一个只写列表类集合(或自定义数据结构)。由于您的数据结构是只写的,您可以使它成为 逆变——也就是说,WriteOnlyThing[Parent]
将是 WriteOnlyThing[Child]
的子类型。然后让 func
接受 WriteOnlyThing[Child]
并且可以安全地传入 WriteOnlyThing[Child]
和 WriteOnlyThing[Parent]
的实例。
如果这两种方法都不适用于您的情况,您唯一的办法是使用 # type: ignore
来消除错误(不推荐),放弃对列表内容进行类型检查并使参数为输入 List[Any]
(同样不推荐),或弄清楚如何重构代码以使其类型安全。