Description
(There are a couple of other issues that I think might be the same thing as this question, but I'm not sure, because they're posed at a lower level—apologies if this is actually a duplicate. #3153, maybe?)
The generic methods and generic self docs show how write a class with methods that accept/return arguments of the most precise type, so things like this work as intended:
from typing import Generic, TypeVar
T = TypeVar('T', bound='Modular')
### this works! yay!
class Modular(object):
modulus = 0 # type: int
def __init__(self, i):
# type: (int) -> None
self.i = i % self.modulus
def __add__(self, other):
# type: (T, T) -> T
return self.__class__((self.i + other.i) % self.modulus)
class Mod5(Modular):
modulus = 5
class Mod6(Modular):
modulus = 6
Mod5(3) + Mod6(3) # statically "disallowed"
mypy won't let me add Mod5
s to Mod6
s, hooray. But what if the relevant class is itself generic? This doesn't work:
In = TypeVar('In', contravariant=True)
Out = TypeVar('Out', covariant=True)
A = TypeVar('A')
B = TypeVar('B')
F = TypeVar('F', bound='FunctionLike')
class FunctionLike(Generic[In, Out]):
def call(self, arg):
# type: (In) -> Out
raise NotImplementedError
def compose(self, other):
# type: (F[In, Out], F[Out, B]) -> F[In, B] # can't index `F`, so this doesn't work
raise NotImplementedError
The type of compose
shouldn't be (FunctionLike[In, Out], FunctionLike[Out, B]) -> FunctionLike[In, B]
, because then any two instances of any two subclasses could be composed together. It can't be (F, F) -> F
because then all the type parameters are the same.
Is there a way to express this?
Activity
elazarg commentedon Dec 19, 2017
Related: #2354
ilevkivskyi commentedon Dec 19, 2017
Many aspects of #2354 and #3153 can be fixed (and I actually wanted to do this soon).
But, your particular feature request is too tricky. This is what is known in other languages as higher kinds. A normal generic is something that expects a type (or several types) and "returns" a type. While in your case,
FunctionLike.compose
is something that itself expects a type constructor (F
in your case) that takes and "returns" a type. If you are familiar wit Haskell, then normal generics (supported by mypy) are* -> *
,* -> * -> *
(two type variables), etc. You want something like(* -> *) -> * -> *
(if I fixIn
andOut
for simplicity). Supporting this is more tricky (especially the type inference), so don't expect this in the near future. Fortunately, in Python it is OK to writeAny
(or other type less precise than you would want).msullivan commentedon Jan 28, 2020
Closing this in favor of the other issues linked