如何从泛型列表中提取泛型实参的联合类型?
解决泛型列表的类型兼容问题与联合类型推断
一、修复MyPy报错的核心方案
你遇到的报错是因为默认的TypeVar是不变的,A[int]和A[int | str]被视为完全不兼容的类型。要解决这个问题,只需将泛型参数定义为协变的:
from typing import Generic, TypeVar # 定义协变的TypeVar,允许从更具体的泛型实例转换到更宽泛的 T = TypeVar("T", covariant=True) class A(Generic[T]): t: T def __init__(self, t: T): self.t = t class B(Generic[T]): def foo(self, x: list[A[T]]) -> A[T]: return x[0] a1 = A[int](3) a2 = A[str]('3') # 此时MyPy不再报错,因为协变特性允许A[int]赋值给A[int | str] b = B[int | str]().foo([a1, a2])
协变的含义是:如果X是Y的子类型,那么A[X]也是A[Y]的子类型。这里int和str都是int | str的子类型,所以对应的A实例可以放入list[A[int | str]]中。
二、实现「列表泛型实参的联合类型推断」
针对你最初的Foo类需求,需要将Foo定义为泛型类,结合协变泛型参数,让类型检查器自动推断列表中所有泛型实参的联合类型:
from typing import Generic, TypeVar, List # 同样用协变TypeVar保证兼容性 T = TypeVar("T", covariant=True) class MyType(Generic[T]): value: T def __init__(self, value: T): self.value = value class Foo(Generic[T]): def __init__(self, vals: List[MyType[T]]): self.vals = vals # 使用示例:传入不同泛型参数的MyType实例 foo = Foo([MyType[int](1), MyType[str]("hello"), MyType[bool](True)]) # MyPy会自动推断foo的类型为 Foo[int | str | bool]
进阶:捕获可变数量的泛型参数(Python 3.11+)
如果你需要更精确地捕获每个元素的泛型参数(而非直接合并为联合类型),可以使用Python 3.11新增的TypeVarTuple和Unpack特性:
from typing import Generic, TypeVar, TypeVarTuple, Unpack Ts = TypeVarTuple("Ts") T = TypeVar("T") class MyType(Generic[T]): value: T def __init__(self, value: T): self.value = value class Foo(Generic[Unpack[Ts]]): def __init__(self, vals: list[MyType[Unpack[Ts]]]): self.vals = vals # 此时foo的类型为 Foo[int, str, bool],可通过Union[Unpack[Ts]]获取联合类型 foo = Foo([MyType[int](1), MyType[str]("hello"), MyType[bool](True)])
内容的提问来源于stack exchange,提问作者Rob
相关产品推荐
相关产品推荐

