如何为容器类中的模型与数据指定关联类型注解?
问题描述
现有多个模型类,每个模型类对应处理特定的数据类,示例代码如下:
from dataclasses import dataclass from typing import Any, Protocol, final @dataclass class Data(Protocol): val_generic: int @dataclass class DataA(Data): val_generic: int = 1 val_a: int = 2 @dataclass class DataB(Data): val_generic: int = 4 val_b: int = 1 class ModelA: def update(self, data: DataA) -> None: data.val_a = data.val_a + data.val_generic class ModelB: def update(self, data: DataB) -> None: data.val_b = data.val_b + data.val_generic
需要实现一个Container类统一管理数据和模型,但当前Container的model参数只能用Any注解,无法让类型检查器识别合法调用并拦截错误组合:
@final class Container: def __init__(self, data: Data, model: Any): self.data = data self.model = model def update(self): self.model.update(self.data) # 合法调用 model_a = ModelA() data_a = DataA() container1 = Container(data_a, model_a) container1.update() # 运行正常 # 错误调用(运行时会抛出AttributeError,但类型检查器无法提前识别) data_b = DataB() container2 = Container(data_b, model_a) container2.update()
尝试过定义Model协议,但因ModelA.update和ModelB.update的参数签名不同,无法编写通用的抽象update方法。需要通过泛型实现类型绑定,让类型检查器能验证Container.__init__的data和model类型必须匹配。
解决方案
可以通过泛型+带关联类型的Protocol实现类型绑定,让类型检查器自动验证数据与模型的匹配关系,具体实现如下:
from dataclasses import dataclass from typing import Protocol, final, TypeVar, Generic # 定义绑定Data协议的类型变量 D = TypeVar('D', bound='Data') class Data(Protocol): val_generic: int @dataclass class DataA(Data): val_generic: int = 1 val_a: int = 2 @dataclass class DataB(Data): val_generic: int = 4 val_b: int = 1 # 定义带泛型关联的Model协议 class Model(Protocol[D]): def update(self, data: D) -> None: ... class ModelA: def update(self, data: DataA) -> None: data.val_a = data.val_a + data.val_generic class ModelB: def update(self, data: DataB) -> None: data.val_b = data.val_b + data.val_generic # 泛型Container类,绑定Data类型与对应Model类型 @final class Container(Generic[D]): def __init__(self, data: D, model: Model[D]): self.data = data self.model = model def update(self): self.model.update(self.data) # 合法调用:类型检查器无报错 model_a = ModelA() data_a = DataA() container1 = Container(data_a, model_a) container1.update() # 错误调用:类型检查器会直接提示类型不兼容 data_b = DataB() container2 = Container(data_b, model_a) # 此处报错:ModelA不符合Model[DataB]的协议要求 container2.update()
关键说明
- 类型变量D:通过
TypeVar('D', bound='Data')限定D必须是Data协议的实现类,确保数据类型的合法性。 - 泛型Model协议:
Model[D]通过泛型关联了update方法的参数类型,只要模型类的update方法接受对应的数据类型,就会自动符合该协议,无需显式继承。 - 泛型Container:继承
Generic[D]后,__init__方法强制要求data: D和model: Model[D],让类型检查器自动验证两者的类型绑定关系,非法组合会在编码阶段被拦截。
内容的提问来源于stack exchange,提问作者Durtal
相关产品推荐
相关产品推荐

