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](或AB 的任何子类型的联合,例如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) 并不正确,知道值限制 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的类型变量,恰好是协变的。它不应该与联合有界的情况相同。 【参考方案1】:

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

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

    我们有几个“常规”、“面向对象”类,BaseDerived。 我们还有一些泛型类型 - 比如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) 是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 python 中继承类型提示?

Python 类型提示:Callable 后跟 TypeVar 是啥意思?

python输入三个数从小到大排序

python输入eval

python输入eval

Matrix 的 Python Generic 类,我收到无法实例化打字。TypeVar 错误