首页 > 解决方案 > TypeVar('T', A, B) 和 TypeVar('T', bound=Union[A, B]) 的区别

问题描述

我正在努力理解以下两个TypeVars 之间的区别:

from typing import TypeVar, Union

class A: pass
class B: pass

T = TypeVar("T", A, B)
T = TypeVar("T", bound=Union[A, B])

有人想开导我吗?


作为我不明白的一个例子:这通过了类型检查......

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

...但是T = TypeVar("T", A, B),它失败了

错误:“X”的类型变量“T”的值不能是“AA”


相关:关于和之间区别的这个问题Union[A, B]TypeVar("T", A, B)

标签: pythongenericstype-hintingpython-typing

解决方案


当你这样做时T = TypeVar("T", bound=Union[A, B]),你是在说 T 可以绑定到其中一个Union[A, B]或任何子类型Union[A, B]。它是联合的上限

例如,如果你有一个 type 的函数def f(x: T) -> T,那么传入以下任何类型的值都是合法的:

  1. Union[A, B](或 A 和 B 的任何子类型的并集,例如Union[A, BChild]
  2. A(或 A 的任何亚型)
  3. B(或 B 的任何亚型)

这就是泛型在大多数编程语言中的行为方式:它们允许您强加一个上限。


但是当你这样做时T = TypeVar("T", A, B),你基本上是在说T 必须是 A 的上限或 B 的上限。也就是说,不是建立一个单一的上限,而是建立多个!

所以这意味着虽然传入类型ABinto的值是合法的f,但传入是合法的,Union[A, B]因为联合既不是 A 也不是 B 的上限。


例如,假设您有一个可包含 int 或 strs 的可迭代对象。

如果您希望此可迭代对象包含任何整数或 strs 的任意混合,则只需要 a 的单个上限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)

相反,如果您想强制该函数接受所有 int所有 str的列表,但绝不接受两者的混合,则需要多个上限。

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)

推荐阅读