Python 3类型注释和子类

33

在Python类型注释中,如何引用“任何子类化父类的对象”?

例如:FooBase是一个抽象基类,Foo1Foo2等都是其子类。我希望该函数能接受FooBase的任何后代。下面这样写是否正确:

def do_something(self, bar:FooBase):
    pass

或者它只接受FooBase类的对象,但是这是不可能的,因为FooBase是抽象的?那么,我需要构建所有情况的Union(请神明,希望不用这样做!),还是可以通过其他方式抽象地表示这种关系?

3个回答

20

继承同样适用于带注释的类型。任何Foo的实例,只要它是FooBase的子类型,也是FooBase类型的有效对象。因此,你可以将FooBase对象和Foo对象都传递给该函数。

如果你想将该函数限制为FooBar的子类,可以看一下Type[C]结构:类对象的类型


11
使用TypeVar解决,并且PyCharm检查器接受它。
from pydantic import BaseModel
class MyModel(BaseModel):
    pass


from typing import TypeVar
BaseModelType = TypeVar('BaseModelType', bound='BaseModel')

async def do_smth(value: BaseModelType):
    pass

# ...

await do_smth(MyModel())

9
这个只接受一个FooBase类的对象吗?不是,它也会接受任何子类。这也在类型提示PEP的理论中说明了,特别是渐进式类型化部分的总结
一个类型t1与类型t2一致,如果t1t2的子类型。(但反过来不成立)。
在处理类型提示时,请参考该文档获取更多指针。
我需要构建所有情况的联合吗?
即使您这样做了,所有子类也将从Union中消除,并且子类将被跳过。尝试创建您提到的Union
typing.Union[Foo1, Foo2, FooBar]

结果应该是FooBar。它是一个抽象类在这里没有任何区别,Python本身在typing模块中使用许多抽象类。

Sized abc为例; 使用Sized提示函数允许任何虚拟子类(定义__len__的类)进行替换:

def foo(obj: Sized): pass

foo([1, 2, 3, 4]) # ok
foo([2, 3, 4, 5]) # ok

我对此感到困惑。我有一个基类,它继承自 pytorchvisionVisionDataset。我将这个类继承到了几个子类 ABC 中。如果我测试 typing.Union[A, B, C, BaseClass],那么这四个类都会出现。这很奇怪。 编辑:我的错,我使用了类构造函数。所以我的类型是 type - MaKaNu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接