我试图了解绑定变量在 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]"
有人可以解释为什么这不起作用,以及是否有任何解决方法?
T = TypeVar("T", bound=X, covariant=True)
虽然
Y
是 X
的子类型,但当且仅当 A 是协变时,T[Y]
是 T[X]
的子类型。请参阅此处。通过将泛型类型 T
指定为协变,警告就会消失。
如果有时间,我会尝试回来详细说明。
另一个答案解释了解决方案并将您链接到正确的数学关键字,因此我只会回答“为什么这不起作用?”的问题
狗的列表不是动物的列表吗?
List[Dog]
是 List[Animal]
的子类型。你可能会认为是的。但是,编程语言中的一种类型成为另一种类型的子类型意味着什么?子类型必须在可以使用父类型的任何地方都可用,这就是里氏替换原则(SOLID 的 L)。当一个函数需要一个 List[Animal]
时,你可以在其中放入一个 List[Dog]
吗?不,不一定!考虑一下该函数将一只猫添加到 List[Animal]
的场景,这对于 List[Dog]
不起作用。
协方差 意味着类型变量的行为方式使得
List[Dog]
可以被视为 List[Animal]
的子类型。对于列表,如果您仅对列表执行读取操作,就会出现这种情况。
有关此主题的更深入解释,请阅读此非 Python 问题(您无需了解任何 Java 即可遵循):
List
为了理解协变/逆变,请看这个优秀的答案及其漂亮的图片,它可以让你跳过一些数学文本。