Given a class with a helper method for initialization:
class TrivialClass:
def __init__(self, str_arg: str):
self.string_attribute = str_arg
@classmethod
def from_int(cls, int_arg: int) -> ?:
str_arg = str(int_arg)
return cls(str_arg)
Is it possible to annotate the return type of the from_int
method?
I'v tried both cls
and TrivialClass
but PyCharm flags them as unresolved references which sounds reasonable at that point in time.
cls accepts the class Person as a parameter rather than Person's object/instance. Now, we pass the method Person. printAge as an argument to the function classmethod . This converts the method to a class method so that it accepts the first parameter as a class (i.e. Person).
In short, a TypeVar is a variable you can use in type signatures so you can refer to the same unspecified type more than once, while a NewType is used to tell the type checker that some values should be treated as their own type.
Use a generic type to indicate that you'll be returning an instance of cls
:
from typing import Type, TypeVar
T = TypeVar('T', bound='TrivialClass')
class TrivialClass:
# ...
@classmethod
def from_int(cls: Type[T], int_arg: int) -> T:
# ...
return cls(...)
Any subclass overriding the class method but then returning an instance of a parent class (TrivialClass
or a subclass that is still an ancestor) would be detected as an error, because the factory method is defined as returning an instance of the type of cls
.
The bound
argument specifies that T
has to be a (subclass of) TrivialClass
; because the class doesn't yet exist when you define the generic, you need to use a forward reference (a string with the name).
See the Annotating instance and class methods section of PEP 484.
Note: The first revision of this answer advocated using a forward reference naming the class itself as the return value, but issue 1212 made it possible to use generics instead, a better solution.
As of Python 3.7, you can avoid having to use forward references in annotations when you start your module with from __future__ import annotations
, but creating a TypeVar()
object at module level is not an annotation. This is still true even in Python 3.10, which defers all type hint resolution in annotations.
From Python 3.7 you can use __future__.annotations
:
from __future__ import annotations
class TrivialClass:
# ...
@classmethod
def from_int(cls, int_arg: int) -> TrivialClass:
# ...
return cls(...)
Edit: you can't subclass TrivialClass
without overriding the classmethod, but if you don't require this then I think it's neater than a forward reference.
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