mypy:创建一个接受子类实例列表的类型 [英] mypy: creating a type that accepts list of instances of subclasses
问题描述
假设我有一个 Child
类,它是 Parent
类的子类,以及一个接受 Parent
子类实例列表的函数:
Suppose I have a Child
class that is a subclass of Parent
class, and a function that accepts a list of instances of Parent
subclasses:
from typing import List
class Parent:
pass
class Child(Parent):
pass
def func(objects: List[Parent]) -> None:
print(objects)
children = [Child()]
func(children)
在上面运行 mypy
会产生一个错误:
running mypy
on this produces an error:
error: Argument 1 to "func" has incompatible type "List[Child]"; expected "List[Parent]"
如何为此创建类型?
附言有一种方法可以使用 Sequence
类型修复此特定错误:
P.S. There's a way to fix this particular error with a Sequence
type:
def func(objects: Sequence[Parent]) -> None:
print(objects)
但这在其他类似情况下无济于事.我需要一个 List
,而不是 Sequence
.
but this doesn't help in other similar cases. I need a List
, not a Sequence
.
推荐答案
在这里传入一个列表从根本上来说不是类型安全的.例如,如果您这样做会怎样?
Passing in a list here is fundamentally not type-safe. For example, what if you do this?
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()!
如果允许进行类型检查,您的代码最终会包含错误.
If this were permitted to type check, your code would end up containing a bug.
使用类型行话,List
被有意设计为不变类型.也就是说,即使 Child
是 Parent
的子类,List[Child]
也不是 的子类List[Parent]
,反之亦然.您可以在此处找到有关不变性的更多信息此处.
To use type-jargon, List
is intentionally designed to be an invariant type. That is, even though Child
is a subclass of Parent
, it is not the case that List[Child]
is a subtype of List[Parent]
, or vice-versa. You can find more info about invariance here and here.
最常见的替代方法是使用 Sequence
代替,它是一个只读接口/协议/任何东西.并且由于 Sequence 是只读的,所以它协变是安全的:也就是说,Sequence[Child]
被认为是 Sequence[ 的有效子类型父]
.
The most common alternative is to use Sequence
instead, which is a read-only interface/protocol/whatever. And since Sequence is read-only, it's safe for it to be covariant: that is, Sequence[Child]
is considered to be a valid subtype of Sequence[Parent]
.
根据您的具体操作,您可以使用 输入变量.例如.与其说这个函数接受一个 Parent 的列表",不如说这个函数接受一个包含 Parent 或 Parent 子类的任何类的列表":
Depending on what exactly you're doing, you may be able to use type variables instead. E.g. instead of saying "this function takes in a list of Parent", you say "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
根据您的具体操作,您可以 maaaaaaaaybe 创建一个自定义协议 定义一个只写的类似列表的集合(或自定义数据结构).由于你的数据结构是只写的,你可以让它逆变——也就是说,WriteOnlyThing[Parent]
将是 WriteOnlyThing[Child] 的子类型]代码>.然后让
func
接受 WriteOnlyThing[Child]
并且可以安全地传入 WriteOnlyThing[Child]
和 WriteOnlyThing[Parent] 的实例代码>.
Depending on what exactly you're doing, you could maaaaaaaaybe create a custom Protocol that defines a write-only list-like collection (or a custom data structure). And since your data structure would be write-only, you could make it contravariant -- that is, WriteOnlyThing[Parent]
would be a subtype of WriteOnlyThing[Child]
. You then make func
accept WriteOnlyThing[Child]
and could safely pass in instances of both WriteOnlyThing[Child]
and WriteOnlyThing[Parent]
.
如果这两种方法都不适用于您的情况,您唯一的办法就是使用 # type: ignore
来消除错误(不推荐),放弃对列表内容进行类型检查并使用 List[Any]
类型的参数(也不推荐),或者找出如何重构代码以使其类型安全.
If neither approach works in your case, your only recourse is to either use # type: ignore
to silence the error (not recommended), give up on type-checking the contents of the list and make the argument of type List[Any]
(also not recommended), or figure out how to restructure your code so it's type-safe.
这篇关于mypy:创建一个接受子类实例列表的类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!