Chris vCB
Chris vCB

Reputation: 1053

Python 3 type annotations and subclasses

How do I refer to 'any object that subclasses a parent class' in Python type annotations?

Example: FooBase is an abstract base class, from which Foo1, Foo2, etc. are subclassed. I want the function to accept any descendant of FooBase. Will this do:

def do_something(self, bar:FooBase):
    pass

Or will this only accept an object of class FooBase, which of course is impossible given that FooBase is abstract? In that case, do I need to build a Union of all cases (please God I hope not!), or can I through some other way express this relationship abstractly?

Upvotes: 37

Views: 21461

Answers (3)

madzohan
madzohan

Reputation: 11808

Solved using TypeVarand PyCharm checker accepts it:

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())

Upvotes: 12

Dimitris Fasarakis Hilliard
Dimitris Fasarakis Hilliard

Reputation: 160427

will this only accept an object of class FooBase?

No, this will accept any subclasses too. This is also stated in the Theory of Type Hinting PEP, specifically the summary of Gradual Typing section:

A type t1 is consistent with a type t2 if t1 is a subtype of t2. (But not the other way around.)

take a look at it for further pointers when dealing with type hints.

do I need to build a Union of all cases.

Even if you did, all subclasses will be eliminated from the Union and the subclasses are going to get skipped. Try creating the Union you mention:

typing.Union[Foo1, Foo2, FooBar]

and the result should be FooBar. The fact that it is an abstract class pays no difference here, Python itself uses many abstract classes in the typing module.

Take for example the Sized abc; hinting a function with Sized allows any virtual subclass (classes defining __len__) to be substituted:

def foo(obj: Sized): pass

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

Upvotes: 10

poke
poke

Reputation: 387687

Inheritance also applies to annotated types. Any instance of Foo which is a subtype of FooBase is also a valid object of the type FooBase. So you can pass a FooBase object but also a Foo object to the function.

If you want to limit the function to only subclasses of FooBar, you could take a look at the Type[C] construct: The type of class objects.

Upvotes: 20

Related Questions