Joe*_*ley 16 python generics type-hinting
我正在努力弄清楚以下两个TypeVars之间的区别
from typing import TypeVar, Union
class A: pass
class B: pass
T = TypeVar("T", A, B)
T = TypeVar("T", bound=Union[A, B])
Run Code Online (Sandbox Code Playgroud)
有人想启发我吗?
一个我不明白的例子......
T = TypeVar("T", bound=Union[A, B])
class AA(A):
pass
class X(Generic[T]):
pass
class XA(X[A]):
pass
class XAA(X[AA]):
pass
Run Code Online (Sandbox Code Playgroud)
通过类型检查,但T = TypeVar("T", A, B)失败了
错误:“X”的类型变量“T”的值不能是“AA”
相关:this question on the difference between Union[A, B]andTypeVar("T", A, B)
Mic*_*x2a 28
当您这样做时T = TypeVar("T", bound=Union[A, B]),您是说 T 可以绑定到 的任一Union[A, B]或任何子类型Union[A, B]。它是联合的上限。
因此,例如,如果您有一个 type 函数def f(x: T) -> T,则传入以下任何类型的值都是合法的:
Union[A, B](或 A 和 B 的任何子类型的联合,例如Union[A, BChild])A (或 A 的任何亚型)B (或 B 的任何亚型)这就是泛型在大多数编程语言中的行为方式:它们让你强加一个单一的上限。
但是当你这样做时T = TypeVar("T", A, B),你基本上是说T must要么由 A 为上限,要么由 B 为上限。也就是说,不是建立单个上限,而是建立多个上限!
因此,这意味着虽然这将是法律在任何类型的值传递A或B进入f,这将不会是合法的通过Union[A, B],因为工会是由A和B.没有上界
例如,假设您有一个可以包含 int 或 strs 的可迭代对象。
如果您希望这个可迭代对象包含任意整数或字符串的混合,您只需要一个Union[int, str]. 例如:
from typing import TypeVar, Union, List, Iterable
mix1: List[Union[int, str]] = [1, "a", 3]
mix2: List[Union[int, str]] = [4, "x", "y"]
all_ints = [1, 2, 3]
all_strs = ["a", "b", "c"]
T1 = TypeVar('T1', bound=Union[int, str])
def concat1(x: Iterable[T1], y: Iterable[T1]) -> List[T1]:
out: List[T1] = []
out.extend(x)
out.extend(y)
return out
# Type checks
a1 = concat1(mix1, mix2)
# Also type checks (though your type checker may need a hint to deduce
# you really do want a union)
a2: List[Union[int, str]] = concat1(all_ints, all_strs)
# Also type checks
a3 = concat1(all_strs, all_strs)
Run Code Online (Sandbox Code Playgroud)
相反,如果您想强制该函数接受所有整数或所有字符串的列表,但绝不接受两者的混合,则需要多个上限。
T2 = TypeVar('T2', int, str)
def concat2(x: Iterable[T2], y: Iterable[T2]) -> List[T2]:
out: List[T2] = []
out.extend(x)
out.extend(y)
return out
# Does NOT type check
b1 = concat2(mix1, mix2)
# Also does NOT type check
b2 = concat2(all_ints, all_strs)
# But this type checks
b3 = concat2(all_ints, all_ints)
Run Code Online (Sandbox Code Playgroud)
经过大量阅读后,我相信 mypy 正确地提出了type-varOP问题中的错误:
generics.py:31: 错误:“X”的类型变量“T”的值不能是“AA”
请参阅下面的解释。
第二种情况:TypeVar("T", bound=Union[A, B])
我认为@Michael0x2a 的回答很好地描述了正在发生的事情。
第一个案例:TypeVar("T", A, B)
原因归结为里氏替换原则(LSP),也称为行为子类型化。解释这一点超出了本答案的范围,您需要阅读并理解invariancevs的含义covariance。
默认情况下,类型变量是不变的。
基于此信息,T = TypeVar("T", A, B)意味着类型变量具有类和T的值限制,但因为它是不变的......它只接受这两个(而不是或 的任何子类)。ABAB
因此,当传递时AA, mypy 会正确地引发type-var错误。
然后您可能会说:好吧,这与AA的行为子类型不正确匹配吗A?在我看来,你是对的。
为什么?因为我们可以正确地替换 out 和Awith AA,并且程序的行为将保持不变。
但是,由于 mypy 是静态类型检查器,因此 mypy 无法弄清楚这一点(它无法检查运行时行为)。人们必须通过语法明确地声明协方差covariant=True。
另请注意:指定协变时TypeVar,应在类型变量名称中使用后缀_co。这记录在PEP 484中。
from typing import TypeVar, Generic
class A: pass
class AA(A): pass
T_co = TypeVar("T_co", AA, A, covariant=True)
class X(Generic[T_co]): pass
class XA(X[A]): pass
class XAA(X[AA]): pass
Run Code Online (Sandbox Code Playgroud)
输出:Success: no issues found in 1 source file
那你该怎么办?
我会使用TypeVar("T", bound=Union[A, B]), 因为:
A并且B不相关进一步阅读 mypy 中 LSP 相关问题:
| 归档时间: |
|
| 查看次数: |
5440 次 |
| 最近记录: |