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 asType[C]
whereC
is a class. In
other words, whenC
is the name of a class, usingC
to annotate an
argument declares that the argument is an instance ofC
(or of a
subclass ofC
), but usingType[C]
as an argument annotation declares
that the argument is a class object deriving fromC
(orC
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 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
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
How to "Zip Sort" Parallel Numpy Arrays
Take Multiple Lists into Dataframe
Preprocessing in Scikit Learn - Single Sample - Depreciation Warning
Type Hints with User Defined Classes
Django Model Field Default Based Off Another Field in Same Model
How to Upload a File to Google Cloud Storage on Python 3
Numpy.Where() Detailed, Step-By-Step Explanation/Examples
Iso to Datetime Object: 'Z' Is a Bad Directive
How to Left Align a Fixed Width String
How to Draw Axis in the Middle of the Figure
Using Self.Xxxx as a Default Parameter - Python
MySQL "Incorrect String Value" Error When Save Unicode String in Django
Matrix Multiplication in Pure Python
How to Normalize a Url in Python
Matplotlib: How to Draw a Rectangle on Image
Convert Bytes to Bits in Python