MyPy 不允许受约束的 TypeVar 协变?使用受约束但协变的键值类型定义通用字典
MyPy doesn't allow constrained TypeVar's to be covariant? Defining a generic dict with constrained but covariant key-val types
我正在尝试定义一个自定义通用字典,其键的类型为 T_key
,值的类型为 T_val
。
我还想对 T_key
和 T_val
施加约束,这样 T_key
只能是 A
或 B
类型或其子类。
我该如何完成?
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]
当我尝试检查这个时,mypy 在 x
、y
和 z
的注释上给出了错误。只有 w
的注释按预期工作。
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"
我不明白为什么 Asub
不是 T_key
的有效类型,即使指定了 covariant=True
。
我在这里错过了什么?
mypy version: 0.630
这不是协方差的意思。对于协变类型变量 T
和泛型 class Foo[T]
,Foo[Subclass]
的实例也被视为 Foo[Superclass]
的实例。协方差对可替代 T
.
的类型没有影响
如果您的B
被定义为
class B(A): ...
而不是
class B: ...
,那么由于协变性,类型 MyDict[B, B]
的值将被静态类型检查器视为类型 MyDict[A, A]
的有效值。您仍然无法创建 MyDict[ASub, BSub]
类型的值,因为类型变量的唯一有效值是 A
和 B
.
您要查找的概念是 有界 类型变量,使用 bound
关键字参数,而不是约束类型变量。看起来你可以指定一个联合作为边界,这让我很惊讶,所以将类型变量声明为
T_key = TypeVar('T_key', bound=Union[A, B])
T_val = TypeVar('T_val', bound=Union[A, B])
应该可以。
解决方案:
事实证明绑定可以接受联合。
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]"
我正在尝试定义一个自定义通用字典,其键的类型为 T_key
,值的类型为 T_val
。
我还想对 T_key
和 T_val
施加约束,这样 T_key
只能是 A
或 B
类型或其子类。
我该如何完成?
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]
当我尝试检查这个时,mypy 在 x
、y
和 z
的注释上给出了错误。只有 w
的注释按预期工作。
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"
我不明白为什么 Asub
不是 T_key
的有效类型,即使指定了 covariant=True
。
我在这里错过了什么?
mypy version: 0.630
这不是协方差的意思。对于协变类型变量 T
和泛型 class Foo[T]
,Foo[Subclass]
的实例也被视为 Foo[Superclass]
的实例。协方差对可替代 T
.
如果您的B
被定义为
class B(A): ...
而不是
class B: ...
,那么由于协变性,类型 MyDict[B, B]
的值将被静态类型检查器视为类型 MyDict[A, A]
的有效值。您仍然无法创建 MyDict[ASub, BSub]
类型的值,因为类型变量的唯一有效值是 A
和 B
.
您要查找的概念是 有界 类型变量,使用 bound
关键字参数,而不是约束类型变量。看起来你可以指定一个联合作为边界,这让我很惊讶,所以将类型变量声明为
T_key = TypeVar('T_key', bound=Union[A, B])
T_val = TypeVar('T_val', bound=Union[A, B])
应该可以。
解决方案:
事实证明绑定可以接受联合。
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]"