Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inheriting Generic classes with restricted TypeVar

Consider a simple pair of generic classes:

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

class Base(Generic[T]):
    def __init__(self, v: T):
        self.v: T = v

    @property
    def value(self) -> T:
        return self.v

class Child(Base[T]):
    def __init__(self, v: T):
        super().__init__(v)

x = Child(123)
reveal_type(x.value)

While using T = TypeVar("T") works as expected. The restricted TypeVar as shown yields the following errors:

error: Argument 1 to "__init__" of "Base" has incompatible type "str"; expected "T"
error: Argument 1 to "__init__" of "Base" has incompatible type "int"; expected "T"
note: Revealed type is 'builtins.int*'

Note that reveal_type still works.

Another difference, is that the restricted TypeVar requires the type annotation for self.v assignment whereas the unrestricted does not.

In the full use-case, I actually have Callable[[Any], T], but the issue is the same.

This is with mypy 0.910 and Python 3.9.7.

like image 750
Zulan Avatar asked Oct 26 '22 09:10

Zulan


2 Answers

Bounding T to an Union[int,str] should do the job:

T = TypeVar("T", bound=Union[str, int])


class Base(Generic[T]):

    def __init__(self, v: T):
        self.v: T = v

    @property
    def value(self) -> T:
        return self.v


class Child(Base[T]):

    def __init__(self, v: T):
        super().__init__(v)


x = Child(123)
reveal_type(x.value)
y = Child('a')
reveal_type(y.value)
like image 159
hussic Avatar answered Oct 30 '22 00:10

hussic


This seems to happen anytime you call super() and pass the a generic restricted TypeVar argument to any function not just init. On those cases, I believe we can ask mypy to leave this poor thing as it is, by including # type: ignore in those lines:

   ...
   super().__init__(v) # type: ignore
   ...

It is not so bad as it seems because type checking will still be done on other lines, differently of what would happen if you remove the : T from the parameter on parent function or on the child function (any of those would suffice to suppress complains, but would also stop checking inside those functions as well).

like image 37
brunoff Avatar answered Oct 29 '22 23:10

brunoff