Python中的typing模块,TypeVar(A, B, covariant=True)是什么意思?

51

今天我深入研究了Liskov替换原则和协变/逆变。

我卡在以下两种方式的区别上:

  1. T = TypeVar("T", bound=Union[A, B])
  2. T = TypeVar("T", A, B, covariant=True)

#1的理解

TypeVar('T', A, B)和TypeVar('T', bound=Union[A, B])的区别

这个答案清楚地说明了T可以是:

  1. Union[A, B](或任何AB的子类型的联合,例如Union[A, BChild]
  2. A(或A的任何子类型)
  3. B(或B的任何子类型)

这对我来说很有道理。


我对#2的理解存在缺陷

MyPy不允许受限制的TypeVar协变吗?定义具有受限但协变键值类型的通用字典

重新提到bound=Union[A, B]情况,但没有涉及选项#2 A、B、covariant=True的含义。

我尝试使用mypy进行实验,但似乎无法弄清楚。 有人能指出这是什么意思吗?

我认为它的意思是:

  1. A(或任何A的子类型)
  2. B(或任何B的子类型)

(即排除上面的Union情况)


**编辑**

评论中有人问:

你确定它们实际上是不同的吗?

这里是展示差异的样例代码。错误来自于 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:当T_co = TypeVar(“T_co”,A,B,covariant=True)且传递了A的子类时,Generic [T_co]出错

这解决了我一些误解。结果发现TypeVar(“T”,A,B,covariant=True)并不正确,因为知道值限制AB实际上不是协变的。

只有当它们相关时,使用covariant=True语法才有用。


你确定它们实际上是不同的吗?联合本身是协变的,所以我认为这两个表达式之间没有任何区别并不明显。 - Samwise
是的,@Samwise 我刚刚编辑了我的问题,以添加演示差异的示例代码。 - Intrastellar Explorer
好的,也许mypy有漏洞。但是,有人能解释一下T = TypeVar("T", A, B, covariant=True)实际上是什么意思吗?你是说它应该与bound=Union[A, B]的情况完全相同,如果是的话,为什么呢? - Intrastellar Explorer
1
应该表示一个只能为AB的类型变量,这恰好是协变的。它不应该与联合边界情况相同。 - user2357112
@user2357112支持Monica,我同意A,ASub,covariant=True应该拒绝SomeOtherASub,因为SomeOtherASub不是ASub的子类。那么对于T = TypeVar(“T”,A,B,covariant=True),然后只使用普通的ASub进行测试呢?你认为它出错是mypy的一个错误吗?我仍然不确定A,B,covariant=True的一般配方。 - Intrastellar Explorer
显示剩余5条评论
1个回答

86

协变性和逆变性是与面向对象和泛型之间的交集相关的术语。

这个概念试图回答以下问题:

  1. 我们有一些“常规”的面向对象类,BaseDerived
  2. 我们还有一些泛型类型 - 假设为 List[T]
  3. 我们知道 Derived 可以在任何可以使用 Base 的地方使用 - 那么这是否意味着 List[Derived] 可以在任何可以使用 List[Base] 的地方使用?
  4. 可能是反过来的吗?也许现在 List[Base] 可以在任何可以使用 List[Derived] 的地方使用?

如果第三个问题的答案是肯定的,那么它被称为协变性,我们将声明 List 具有 covariance=True。如果第四个问题的答案是肯定的,则称为逆变性。如果都不是,则为不变性

边界也来自OO和泛型的交集。当我们定义一个泛型类型MyType<T>时,它是否意味着T可以是任何类型?或者,我可以对T施加一些限制吗?边界允许我声明T的上限是,例如,类Derived。在这种情况下,Base不能与MyType一起使用,但Derived及其所有子类都可以。

协变和逆变的定义可以在PEP-484的本节中找到。


一个简单的快捷方式:它只作为类的方法参数类型使用吗?如果是的话,可能是逆变的。它只作为类的方法返回类型使用吗?如果是的话,可能是协变的。它作为类的属性类型使用,并且这些属性是不可变的吗?如果是的话,可能是协变的。在所有其他情况下,可能是不变的。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接