Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 761
Chris vCB Avatar asked Jan 22 '17 12:01

Chris vCB


People also ask

What is Python type annotation?

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.

What are subclasses Python?

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.

What is __ subclasses __ in Python?

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.

What is typing module in Python?

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.


3 Answers

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.

like image 112
poke Avatar answered Oct 18 '22 03:10

poke


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
like image 27
Dimitris Fasarakis Hilliard Avatar answered Oct 18 '22 03:10

Dimitris Fasarakis Hilliard


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())
like image 8
madzohan Avatar answered Oct 18 '22 02:10

madzohan