如何在Python类型提示中引用TypeVar的泛型类型?
如何在Python中关联泛型父类的子类与其泛型参数类型
假设我有如下代码:
from dataclasses import dataclass from typing import Generic from typing_extensions import TypeVar A = TypeVar('A') B = TypeVar('B') DataVar = TypeVar('DataVar', bound = 'Data') @dataclass class Data(Generic[A, B]): a: A b: B class Object(Generic[DataVar]): def __init__(self, data: DataVar) -> None: super().__init__() self.data: DataVar = data def a(self): # 希望返回DataVar.A的类型 return self.data.a def b(self): # 希望返回DataVar.B的类型 return self.data.b
我需要指定Object.a和Object.b的返回类型为DataVar对应的泛型类型。试过直接让Object继承Generic[A, B]:
class Object(Generic[A, B]): def __init__(self, data: Data[A, B]) -> None: super().__init__() self.data: Data[A, B] = data def a(self) -> A: return self.data.a def b(self) -> B: return self.data.b
这种方式能正确推断返回类型,但无法保留Data的具体子类信息。如果同时声明A、B和DataVar三个泛型参数:
class Object(Generic[A, B, DataVar]): def __init__(self, data: DataVar) -> None: super().__init__() self.data: DataVar = data def a(self) -> A: return self.data.a # 类型检查器无法识别DataVar和A/B的关联 def b(self) -> B: return self.data.b # 同上
类型检查器无法识别DataVar是Data[A,B]的子类,而Python又不支持高阶类型(HKT),有没有其他解决方案?本质是要维护两组泛型对象间的类型关联,目前只能通过调整代码结构解决吗?
解决方案1:用Protocol约束DataVar的类型参数暴露
通过typing.Protocol定义协议,要求Data子类必须暴露自身的泛型参数类型,让Object能通过协议获取A和B的类型:
from dataclasses import dataclass from typing import Generic, Protocol, TypeVar A = TypeVar('A') B = TypeVar('B') DataVar = TypeVar('DataVar', bound='DataProtocol') class DataProtocol(Protocol[A, B]): a: A b: B # 暴露泛型参数类型供外部获取 __type_a__: type[A] __type_b__: type[B] @dataclass class Data(Generic[A, B], DataProtocol[A, B]): a: A b: B # 实现协议要求的类型属性 @property def __type_a__(self) -> type[A]: return type(self.a) @property def __type_b__(self) -> type[B]: return type(self.b) # 示例Data子类 @dataclass class StringIntData(Data[str, int]): extra: str class Object(Generic[DataVar]): def __init__(self, data: DataVar) -> None: self.data: DataVar = data def a(self) -> DataVar.__type_a__: return self.data.a def b(self) -> DataVar.__type_b__: return self.data.b
类型检查器会通过DataProtocol的约束,识别DataVar的__type_a__和__type_b__就是其泛型参数类型,同时保留Data子类的具体类型信息。
解决方案2:绑定DataVar到Data[A,B]并关联泛型参数
在TypeVar的绑定中显式关联A和B,配合类型断言让检查器认可类型关联:
from dataclasses import dataclass from typing import Generic, TypeVar A = TypeVar('A') B = TypeVar('B') # 绑定DataVar为Data[A,B]的子类,同时关联A和B DataVar = TypeVar('DataVar', bound='Data[A, B]') @dataclass class Data(Generic[A, B]): a: A b: B class Object(Generic[A, B, DataVar]): def __init__(self, data: DataVar) -> None: self.data: DataVar = data def a(self) -> A: return self.data.a # type: ignore[return-value] def b(self) -> B: return self.data.b # type: ignore[return-value]
这种方式需要添加类型忽略注释,虽不够优雅,但能同时保留DataVar子类信息和正确的返回类型推断。
解决方案3:针对具体Data子类定义Object子类
如果Object需要针对不同Data子类扩展行为,可以显式定义对应Object子类,让类型检查器完全明确类型关联:
from dataclasses import dataclass from typing import Generic, TypeVar A = TypeVar('A') B = TypeVar('B') DataVar = TypeVar('DataVar', bound='Data[A, B]') @dataclass class Data(Generic[A, B]): a: A b: B @dataclass class StringIntData(Data[str, int]): extra: str class Object(Generic[A, B, DataVar]): def __init__(self, data: DataVar) -> None: self.data: DataVar = data def a(self) -> A: return self.data.a def b(self) -> B: return self.data.b # 针对具体Data子类的Object子类 class StringIntObject(Object[str, int, StringIntData]): def get_extra(self) -> str: return self.data.extra
这种方式适合需要为不同Data子类定制Object逻辑的场景,类型检查会完全准确。
内容的提问来源于stack exchange,提问作者Autumn
相关产品推荐
相关产品推荐

