pythonpython-typing

Subclassing a generic class with constrained type variable in Python 3.12+ without repeating the constraints


I would like to subclass a generic class that is using a constrained type variable. This is fairly easy with pre-3.12 syntax as shown in this minimal example:

from typing import Generic, TypeVar

T = TypeVar("T", int, float)

class Base(Generic[T]):
    pass

class Sub(Base[T]):
    pass

But I am struggling with writing this in the Python 3.12 syntax. I cannot find a way to not have to specify T: (int, float) in the Sub class as well. Below is another minimal example which passes mypy check (version 1.15.0).

class Base[T: (int, float)]:
    pass

class Sub[T: (int, float)](Base[T]):
    pass

But when I change the subclass definition to class Sub[T](Base[T]):, mypy returns error: Type variable "T" not valid as type argument value for "Base" [type-var]. I would expect such definition to be meaningful and I find it unfortunate that I have to define the constraints again, especially when the classes can be in different files and subclassed multiple times.

Am I missing something or is this not fully doable in pure Python 3.12+ syntax?


Solution

  • Unfortunately, you cannot reuse type variables defined with PEP 695 syntax.

    The type variable must have the correct bound to parametrize the superclass (in other words, bounds and constraints can't be inferred or magically "pushed down" from the superclass, that has soundness problems if you consider multiple inheritance). So, you need some way to spell "reuse type variable with these {constraints,bound,default}"... And as you noted in you question, typing.TypeVar is doing exactly that.

    Please note that typing.TypeVar is not deprecated (only typing.TypeAlias is) - new generics syntax is just another way, not a replacement. It will likely stay alive for a very long time if not indefinitely: Eric Traut's comment on that

    It’s also important to note that this PEP doesn’t deprecate the current mechanisms for defining TypeVars or generic classes, functions, and type aliases. Those mechanisms can live side by side with the new syntax. If you find that those mechanisms are more flexible for exploring extensions to the type system, PEP 695 does not prevent you from leveraging these existing mechanisms.

    There are other problems with PEP 695 typevars like inability to specify variance explicitly, so some corner cases would become impossible to express if typing.TypeVar was ever deprecated.

    I'm not aware of any recent proposals or discussions that would enable reusing PEP 695 type variables, and for the reasons mentioned above I doubt that will ever be possible.