Let's say I have two classes Base
and Child
with a factory method in Base
. The factory method calls another classmethod which may be overriden by Base
's child classes.
class Base(object): @classmethod def create(cls, *args: Tuple) -> 'Base': value = cls._prepare(*args) return cls(value) @classmethod def _prepare(cls, *args: Tuple) -> Any: return args[0] if args else None def __init__(self, value: Any) -> None: self.value = value class Child(Base): @classmethod def _prepare(cls, *args: Tuple) -> Any: return args[1] if len(args) > 1 else None def method_not_present_on_base(self) -> None: pass
Is there a way to annotate Base.create
so that a static type checker could infer that Base.create()
returned an instance of Base
and Child.create()
returned an instance of Child
, so that the following example would pass static analysis?
base = Base.create(1) child = Child.create(2, 3) child.method_not_present_on_base()
In the above example a static type checker would rightfully complain that the method_not_present_on_base
is, well, not present on the Base
class.
I thought about turning Base
into a generic class and having the child classes specify themselves as type arguments, i.e. bringing the CRTP to Python.
T = TypeVar('T') class Base(Generic[T]): @classmethod def create(cls, *args: Tuple) -> T: ... class Child(Base['Child']): ...
But this feels rather unpythonic with CRTP coming from C++ and all...
Here's how you can add type hints to our function: Add a colon and a data type after each function parameter. Add an arrow ( -> ) and a data type after the function to specify the return data type.
Factory Method is a creational design pattern used to create concrete implementations of a common interface. It separates the process of creating an object from the code that depends on the interface of the object. For example, an application requires an object with a specific interface to perform its tasks.
The classmethod() is an inbuilt function in Python, which returns a class method for a given function.; Syntax: classmethod(function) Parameter :This function accepts the function name as a parameter. Return Type:This function returns the converted class method.
A Class Factory is a function that creates and returns a class. It is one of the powerful patterns in Python.
It is indeed possible: the feature is called TypeVar with Generic Self (though this is slightly misleading because we're using this for a class method in this case). I believe it behaves roughly equivalently to the "CRTP" technique you linked to (though I'm not a C++ expert so can't say for certain).
In any case, you would declare your base and child classes like so:
from typing import TypeVar, Type, Tuple T = TypeVar('T', bound='Base') class Base: @classmethod def create(cls: Type[T], *args: Tuple[Any]) -> T: ... class Child(Base): @classmethod def create(cls, *args: Tuple[Any]) -> 'Child': ...
Note that:
cls
for the child definition.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