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?
Type annotations — also known as type signatures — are used to indicate the datatypes of variables and input/outputs of functions and methods. In many languages, datatypes are explicitly stated. In these languages, if you don't declare your datatype — the code will not run.
The class from which a class inherits is called the parent or superclass. A class which inherits from a superclass is called a subclass, also called heir class or child class. Superclasses are sometimes called ancestors as well.
New-style classes i.e. subclassed from an object, which is the default in Python 3 have a __subclasses__ method. This method returns the subclasses of the class.
Introduced since Python 3.5, Python's typing module attempts to provide a way of hinting types to help static type checkers and linters accurately predict errors.
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.
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 typet2
ift1
is a subtype oft2
. (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
Solved using TypeVar
and 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())
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With