pythonmypytyping

Is there any relation between classes which use the same type variable?


The typing.TypeVar class allows one to specify reusable type variables. With Python 3.12 / PEP 695, one can define a class A/B with type variable T like this:

class A[T]:
    ...

class B[T]:
    ...

Beforehand, with Python 3.11, you would do it like this:

from typing import TypeVar, Generic

T = TypeVar("T")
class A(Generic[T]):
    ...

class B(Generic[T]):
    ...

For the first example, the T is defined in the class scope, so they do not relate to each other. Is there any difference to the second 'old' example? Or: Is there any connection between the two classes A and B?


Solution

  • There is no such connection from the typechecker's POV. TypeVar is declared outside of class scope just because it's convenient to do so, it does not imply any relationships between its users.

    Type variable is bound in the following scopes:

    No other binding is taking place. This is explained in detail in PEP 484 (link to relevant section).

    However, there may be clean semantic relationship (and it was one of my arguments against PEP695 - a battle I, unfortunately, lost). Consider django-stubs (permalink to the declaration):

    # __set__ value type
    _ST = TypeVar("_ST", contravariant=True)
    # __get__ return type
    _GT = TypeVar("_GT", covariant=True)
    
    class Field(RegisterLookupMixin, Generic[_ST, _GT]):
        ... # omitted (there are 150+ lines here in fact)
    
    class IntegerField(Field[_ST, _GT]):
        ...
    class PositiveIntegerRelDbTypeMixin:
        ...
    class SmallIntegerField(IntegerField[_ST, _GT]): ...
    class BigIntegerField(IntegerField[_ST, _GT]):
        ...
    class PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField[_ST, _GT]): ...
    class PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, SmallIntegerField[_ST, _GT]): ...
    class PositiveBigIntegerField(PositiveIntegerRelDbTypeMixin, BigIntegerField[_ST, _GT]): ...
    

    These fields do not have to resolve to the same type variables, obviously. However, _ST and _GT have a semantic meaning, which is same for all of them. This does not influence type checking, but helps those who read this code. If _GT and _ST were defined for each class separately, then, in addition to unreadable and unpleasantly looking syntax, we'd have to either repeat comments near every class or document that once in module docstring and add one extra lookup step to demystify their meaning. Using longer forms like _SetterType and _GetterType would remove need for explanation, but also make the already long-ish signatures longer. Additionally, explanation of how Field typing works is still necessary (there are other caveats). Now it's explained in a docstring that comes right after typevar declaration, but with PEP695 style it'd be written far away.