Reputation: 1111
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
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