Subclass in Type Hinting

Subclass in type hinting

When you specify cls: A, you're saying that cls expects an instance of type A. The type hint to specify cls as a class object for the type A (or its subtypes) uses typing.Type.

from typing import Type
def process_any_subclass_type_of_A(cls: Type[A]):
pass

From The type of class objects
:

Sometimes you want to talk about class objects that inherit from a
given class. This can be spelled as Type[C] where C is a class. In
other words, when C is the name of a class, using C to annotate an
argument declares that the argument is an instance of C (or of a
subclass of C), but using Type[C] as an argument annotation declares
that the argument is a class object deriving from C (or C itself).

How to type-hint that container subclass always contains particular type?

Use typing.Generic to pass a type to the Box superclass from a subclass using a typing.TypeVar

from typing import Generic, TypeVar

T = TypeVar('T')

class Box(Generic[T], list[T]):
pass

class IntBox(Box[int]):
pass

class StrBox(Box[str]):
pass

ib = IntBox(())
answer1 = ib[0] # int
sb = StrBox(())
answer2 = sb[0] # str

Python type hinting: how to tell X is a subclass for Foo?

It seems like other current (22 Sep 2016) answers here are incorrect. According to PEP 484 (about Type Hints), there exists a hint for type of class objects, called Type[C]. And according to typing module's documentation, you can use typing.Type[C] to achieve exactly what you want. I'm using those myself with Python 3.5.2.

Quoting the PEP:

Sometimes you want to talk about class objects, in particular class objects that inherit from a given class. This can be spelled as Type[C] where C is a class. To clarify: while C (when used as an annotation) refers to instances of class C , Type[C] refers to subclasses of C .

And quoting the docs:

A variable annotated with C may accept a value of type C. In contrast, a variable annotated with Type[C] may accept values that are classes themselves – specifically, it will accept the class object of C.

And referring to your specific example:

import typing

class A(object):
pass

class B(A):
pass

def register(cls: typing.Type[A]):
assert issubclass(cls, A)

register(A)
register(B)

You can check such code statically using mypy, and it should work in simple cases -- beware however that mypy is a work in progress, as of now there are several issues open about Type[C] hinting.

Python 3 type annotations and subclasses

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

Type hint for return value in subclass

Starting in Python 3.11, the correct return annotation for this code is Self:

from typing import Self
class CustomEnum(Enum):
@classmethod
def random(cls) -> Self:
return random.choice(list(cls))

Quoting from the PEP:

This PEP introduces a simple and intuitive way to annotate methods that return an instance of their class. This behaves the same as the TypeVar-based approach specified in PEP 484 but is more concise and easier to follow.

The current workaround for this is unintuitive and error-prone:

Self = TypeVar("Self", bound="Shape")
class Shape:
@classmethod
def from_config(cls: type[Self], config: dict[str, float]) -> Self:
return cls(config["scale"])

We propose using Self directly:

from typing import Self
class Shape:
@classmethod
def from_config(cls, config: dict[str, float]) -> Self:
return cls(config["scale"])

This avoids the complicated cls: type[Self] annotation and the TypeVar declaration with a bound. Once again, the latter code behaves equivalently to the former code.



Related Topics



Leave a reply



Submit