I'm familiar with curiously recurring template pattern (CRTP) implementation in C++ and C# programming languages. But, how we can achieve the same idea in the Python and type-safe (using Mypy)?
This code may not be functioning properly, but symbolically, it provides an idea:
class GenericParent(Generic[T]):
pass
class Derived(GenericParent[Derived]):
pass
I've tried specializing the GenericParent
type variables by the Derived
class but the Mypy
gives error.
As a static typing pattern, CRTP should be supported out of the box across major Python type checking implementations. mypy itself should not give you errors when you declare class Derived(GenericParent[Derived]): ...
(see the following mypy playground demo) - you might be seeing errors from another linter or the IDE you're using, because this code will fail at runtime.
You have several options to make this work. Options 1 and 2 assumes that you don't need runtime introspection; option 3 will handle runtime introspection in a limited fashion.
.pyi
stub file, which has no runtime implementation and thus won't cause any runtime errors. The CRTP pattern is used in Python's own typeshed to statically model the builtin str
type.if typing.TYPE_CHECKING
along with from __future__ import annotations
, which (IMO) is the cleanest way to handle forward references and circular imports in general.import typing as t
T = t.TypeVar("T")
class GenericParent(Generic[T]):
pass
class Derived(GenericParent["Derived"]):
pass
Some helpers from typing
can help perform introspecting this. On Python 3.10:
>>> import typing as t
>>> t._eval_type(Derived.__orig_bases__[0], globals(), {})
__main__.GenericParent[__main__.Derived]