【问题标题】:Python typing what does TypeVar(A, B, covariant=True) mean?Python 输入 TypeVar(A, B, covariant=True) 是什么意思?
【发布时间】:2020-08-17 11:26:04
【问题描述】:

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

我被困在两者之间的区别上:

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

我对#1的理解

Difference between TypeVar('T', A, B) and TypeVar('T', bound=Union[A, B])

This answer 明确指出T 可以:

  1. Union[A, B](或AB 的任何子类型的联合,例如Union[A, BChild]
  2. A(或A 的任何子类型)
  3. 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,但似乎无法弄清楚。 谁能指出这是什么意思?

认为意思是:

  1. A(或A 的任何子类型)
  2. 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) 并不正确,知道值限制 AB 实际上并不是协变的。

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] 的情况相同,如果是,为什么?
  • 应该表示只能是AB的类型变量,恰好是协变的。它不应该与联合有界的情况相同。

标签: python oop covariance mypy static-typing


【解决方案1】:

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

这是这个概念试图回答的问题:

  1. 我们有几个“常规”、“面向对象”类,BaseDerived
  2. 我们还有一些泛型类型 - 比如List<T>
  3. 我们知道 Derived 可以在任何 base 可以使用的地方使用 - 这是否意味着 List<Derived> 可以在任何List<Base> 可以使用的地方使用?
  4. 会不会反过来?也许是相反的方向,现在List<Base> 可以在List<Derived> 可以使用的任何地方使用?

如果 (3) 的答案是肯定的,则称为协方差,我们会说将 List 声明为具有 covariance=True。如果 (4) 的答案为真,则称为“逆变”。如果没有一个为真,则它是不变的。

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

协变和逆变的定义见this section of PEP-484

【讨论】:

    猜你喜欢
    • 2014-02-09
    • 2017-02-02
    • 2013-12-16
    • 2012-06-14
    • 2018-08-06
    • 1970-01-01
    • 2022-11-10
    • 2020-02-09
    • 2010-12-31
    相关资源
    最近更新 更多