mypy:创建一个接受子类实例列表的类型 [英] mypy: creating a type that accepts list of instances of subclasses

查看:48
本文介绍了mypy:创建一个接受子类实例列表的类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个 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 被有意设计为不变类型.也就是说,即使 ChildParent 的子类,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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆