sanders
sanders

Reputation: 1111

TypeVar in class __init__ type hinting

I am trying to use a TypeVar to indicate an init parameter as a certain type. But I am doing it wrong, or it might not even be possible.

from typing import TypeVar

T=TypeVar("T")

class TestClass:
    def __init__(self,value:T):
        self._value=value

a = TestClass(value=10)
b = TestClass(value="abc")

reveal_type(a._value)
reveal_type(b._value)

I was hoping the reveal type of a._value would have been int and b._value to have been string. But they are both revealed as 'T`-1'

Any help or insight appreciated!

[EDIT]

A little more expanded example. The BaseClass will be overridden and the actual type hint is provided by the overriding class.

from typing import TypeVar

T=TypeVar("T")

class BaseClass:
    def __init__(self,value):
        self._value = value

class Class1(BaseClass):
    def __init__(self,value:str):
        super().__init__(value)

class Class2(BaseClass):
    def __init__(self,value:int):
        super().__init__(value)

a = Class1("A value")
b = Class2(10)

reveal_type(a._value)
reveal_type(b._value)

Upvotes: 6

Views: 6924

Answers (1)

MisterMiyagi
MisterMiyagi

Reputation: 52089

By default, using a TypeVar restricts its scope only to the method/function in which it is used as an annotation. In order to scope a TypeVar to the instance and all methods/attributes, declare the class as Generic.

from typing import TypeVar, Generic

T=TypeVar("T")

class BaseClass(Generic[T]):       # Scope of `T` is the class:
    def __init__(self, value: T):  # Providing some `T` on `__init__`
        self._value = value        # defines the class' `T`

This allows declaring subclasses either as generic or as concrete.

class Class1(BaseClass[str]):      # "is a" BaseClass where `T = str`
    pass  # No need to repeat ``__init__``

class ClassT(BaseClass[T]):        # "is a" BaseClass where `T = T'`
    @property
    def value(self) -> T:
        return self._value

reveal_type(Class1("Hello World")._value)  # Revealed type is 'builtins.str*'
reveal_type(Class1(b"Uh Oh!")._value)      # error: Argument 1 to "Class1" has incompatible type "bytes"; expected "str"

reveal_type(ClassT(42).value)              # Revealed type is 'builtins.int*'

Upvotes: 9

Related Questions