问题
I'm trying to define a custom generic dict, whose keys are of type T_key
and values are of type T_val
.
I also want to put constraints on T_key
and T_val
, such that T_key
can only be of type A
or B
or their subclass.
How do I accomplish this?
from typing import TypeVar, Generic
class A: ...
class B: ...
class Asub(A): ...
class Bsub(B): ...
T_key = TypeVar('T_key', A, B, covariant=True)
T_val = TypeVar('T_val', A, B, covariant=True)
class MyDict(Generic[T_key, T_val]): ...
w: MyDict[ A, B]
x: MyDict[ A, Bsub]
y: MyDict[Asub, B]
z: MyDict[Asub, Bsub]
When I try to check this, mypy gives errors on annotations of x
, y
and z
. Only the annotation for w
works as expected.
generic.py:17: error: Value of type variable "T_val" of "MyDict" cannot be "Bsub"
generic.py:18: error: Value of type variable "T_key" of "MyDict" cannot be "Asub"
generic.py:19: error: Value of type variable "T_key" of "MyDict" cannot be "Asub"
generic.py:19: error: Value of type variable "T_val" of "MyDict" cannot be "Bsub"
I don't understand why Asub
is not a valid type for T_key
even with covariant=True
specified.
What am I missing here?
mypy version: 0.630
回答1:
That's not what covariance means. With a covariant type variable T
and a generic class Foo[T]
, instances of Foo[Subclass]
are also considered instances of Foo[Superclass]
. Covariance has no effect on what types may be substituted for T
.
If your B
were defined as
class B(A): ...
instead of
class B: ...
, then a value of type MyDict[B, B]
would be considered a valid value of type MyDict[A, A]
by static type checkers due to covariance. You would still not be able to create a value of type MyDict[ASub, BSub]
, because the only valid values of your type variables are A
and B
.
The concept you're looking for is bounded type variables, using the bound
keyword argument, not constrained type variables. It looks like you can specify a union as the bound, which comes as quite a surprise to me, so declaring the type variables as
T_key = TypeVar('T_key', bound=Union[A, B])
T_val = TypeVar('T_val', bound=Union[A, B])
should work.
回答2:
solution:
Turns out bound can accept Unions.
from typing import TypeVar, Generic, Union
class A: ...
class B: ...
class Asub(A): ...
class Bsub(B): ...
T_key = TypeVar('T_key', bound=Union[A, B])
T_val = TypeVar('T_val', bound=Union[A, B])
class MyDict(Generic[T_key, T_val]): ...
w: MyDict[ A, B] # passes
x: MyDict[ A, Bsub] # passes
y: MyDict[Asub, B] # passes
z: MyDict[Asub, Bsub] # passes
bad: MyDict[int, int] # Type argument "builtins.int" of "MyDict" must be a subtype of "Union[generic.A, generic.B]"
来源:https://stackoverflow.com/questions/52997754/mypy-doesnt-allow-constrained-typevars-to-be-covariant-defining-a-generic-dic