了解具有泛型参数的绑定 TypeVars

问题描述 投票:0回答:2

我试图了解绑定变量在 TypeVars 中如何工作。我知道绑定类的任何子类都是允许的,但是一旦我将绑定类设为泛型,我期望工作的事情就不会了:

from typing import Generic, TypeVar

class X:
    pass
class Y(X):
    pass

T = TypeVar("T", bound=X)

class A(Generic[T]):
    def __init__(self, param: T):
        self.param = param
class B(A[T]):
    pass

S = TypeVar("S", bound=A[X])

def foo(bar: S) -> S:
    return bar

foo(B(Y())) # Type "B[Y]" cannot be assigned to type "A[X]"

有人可以解释为什么这不起作用,以及是否有任何解决方法?

python generics type-hinting
2个回答
1
投票
T = TypeVar("T", bound=X, covariant=True)

虽然

Y
X
的子类型,但当且仅当 A 是协变时,
T[Y]
T[X]
的子类型。请参阅此处。通过将泛型类型
T
指定为协变,警告就会消失。

如果有时间,我会尝试回来详细说明。


0
投票

另一个答案解释了解决方案并将您链接到正确的数学关键字,因此我只会回答“为什么这不起作用?”的问题

狗的列表不是动物的列表吗?

List[Dog]
List[Animal]
的子类型。你可能会认为是的。但是,编程语言中的一种类型成为另一种类型的子类型意味着什么?子类型必须在可以使用父类型的任何地方都可用,这就是里氏替换原则(SOLID 的 L)。当一个函数需要一个
List[Animal]
时,你可以在其中放入一个
List[Dog]
吗?不,不一定!考虑一下该函数将一只猫添加到
List[Animal]
的场景,这对于
List[Dog]
不起作用。

协方差 意味着类型变量的行为方式使得

List[Dog]
可以被视为
List[Animal]
的子类型。对于列表,如果您仅对列表执行读取操作,就会出现这种情况。

有关此主题的更深入解释,请阅读此非 Python 问题(您无需了解任何 Java 即可遵循): List是List的子类吗?为什么 Java 泛型不是隐式多态的?

为了理解协变/逆变,请看这个优秀的答案及其漂亮的图片,它可以让你跳过一些数学文本。

© www.soinside.com 2019 - 2024. All rights reserved.