Python 输入 TypeVar(A, B, covariant=True) 是啥意思?
Posted
技术标签:
【中文标题】Python 输入 TypeVar(A, B, covariant=True) 是啥意思?【英文标题】:Python typing what does TypeVar(A, B, covariant=True) mean?Python 输入 TypeVar(A, B, covariant=True) 是什么意思? 【发布时间】:2020-08-17 11:26:04 【问题描述】:今天我深入研究了 Liskov 的替换原理和协变/逆变。
我被困在两者之间的区别上:
T = TypeVar("T", bound=Union[A, B])
T = TypeVar("T", A, B, covariant=True)
我对#1的理解
Difference between TypeVar('T', A, B) and TypeVar('T', bound=Union[A, B])
This answer 明确指出T
可以:
Union[A, B]
(或A
和B
的任何子类型的联合,例如Union[A, BChild]
)A
(或A
的任何子类型)B
(或B
的任何子类型)
这对我来说很有意义。
我的缺陷对#2的理解
MyPy doesn't allow constrained TypeVar's to be covariant? Defining a generic dict with constrained but covariant key-val types
重新提到bound=Union[A, B]
的情况,但没有理解选项#2 A, B, covariant=True
的含义。
我尝试过使用mypy
,但似乎无法弄清楚。
谁能指出这是什么意思?
我认为意思是:
A
(或A
的任何子类型)
B
(或B
的任何子类型)
(又名它不包括上面的 Union
案例)
**编辑**
在cmets中被问到:
你确定它们真的不同吗?
这里是显示差异的示例代码。错误来自mypy==0.770
。
from typing import Union, TypeVar, Generic
class A: pass
class ASub(A): pass
class B: pass
# Case 1... Success: no issues found
# T = TypeVar("T", bound=Union[A, B])
# Case 2... error: Value of type variable "T" of "SomeGeneric" cannot be "ASub"
T = TypeVar("T", A, B, covariant=True)
class SomeGeneric(Generic[T]): pass
class SomeGenericASub(SomeGeneric[ASub]): pass
**编辑 2**
我最终在python/mypy #8806: Generic[T_co] erroring when T_co = TypeVar("T_co", A, B, covariant=True) and passed subclass of A 中询问了这个问题
这消除了我的一些误解。事实证明 TypeVar("T", A, B, covariant=True)
并不正确,知道值限制 A
和 B
实际上并不是协变的。
covariant=True
语法的使用只有在它们相关时才有用。
【问题讨论】:
你确定它们真的不同吗?联合本身是协变的,所以对我来说这两个表达式之间的区别并不明显。 是的@Samwise 我刚刚编辑了我的问题以添加示例代码来展示差异 我认为 mypy 对带有显式选项的类型变量的处理目前存在问题。使用T = TypeVar("T", A, B)
,即使没有协方差,它也允许x = SomeGeneric[ASub]()
,但不允许x: SomeGeneric[ASub]
。它根本不允许创建 SomeGeneric[ASub]
类型的值。
好的,所以mypy
可能有问题。谁能至少向我解释一下T = TypeVar("T", A, B, covariant=True)
的真正含义?你是说它应该与bound=Union[A, B]
的情况相同,如果是,为什么?
它应该表示只能是A
或B
的类型变量,恰好是协变的。它不应该与联合有界的情况相同。
【参考方案1】:
协变和逆变是与面向对象和泛型之间的交集相关的术语。
这是这个概念试图回答的问题:
-
我们有几个“常规”、“面向对象”类,
Base
和
Derived
。
我们还有一些泛型类型 - 比如List<T>
。
我们知道 Derived 可以在任何 base 可以使用的地方使用 - 这是否意味着 List<Derived>
可以在任何List<Base>
可以使用的地方使用?
会不会反过来?也许是相反的方向,现在List<Base>
可以在List<Derived>
可以使用的任何地方使用?
如果 (3) 的答案是肯定的,则称为协方差,我们会说将 List
声明为具有 covariance=True
。如果 (4) 的答案为真,则称为“逆变”。如果没有一个为真,则它是不变的。
界限也来自于面向对象和泛型的交集。当我们定义一个泛型类型 MyType - 这是否意味着“T”可以是任何类型?或者,我可以对 T 可能是什么施加一些限制吗? Bounds 允许我声明 T 的上限是,例如,Derived
类。在这种情况下,Base
不能与 'MyType' 一起使用 - 但Derived
及其所有子类都可以。
协变和逆变的定义见this section of PEP-484。
【讨论】:
以上是关于Python 输入 TypeVar(A, B, covariant=True) 是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章