如何为动态创建的dataclass添加类型提示?
让mypy识别动态生成的冻结dataclass类型
你的核心问题是通过make_dataclass动态生成类避免重复代码,但mypy无法识别类属性的类型,以下是两种可行的解决思路:
一、用Protocol+cast指定类型
先定义一个与目标类结构匹配的Protocol,再通过cast明确告诉mypy动态生成类的类型:
from enum import Enum from dataclasses import make_dataclass, field, dataclass from typing import Protocol, cast class DatasetNames(Enum): test1 = "test1_string" test2 = "test2_string" test3 = "test3_string" def get_path(s: str) -> str: return s + "_path" # 定义匹配类结构的Protocol class DatasetPathsProtocol(Protocol): test1: str test2: str test3: str # 动态生成类并强制类型转换到Protocol DynamicDatasetPaths = cast( DatasetPathsProtocol, make_dataclass( 'DynamicDatasetPaths', [ ( name.name, str, field(default=get_path(name.value)) ) for name in DatasetNames ], frozen=True ) ) # mypy现在能正确识别DynamicDatasetPaths.test2的类型为str print(DynamicDatasetPaths.test2) # 'test2_string_path'
这种方法需要手动维护Protocol的属性列表,但能让mypy准确推断类型,同时保留动态生成代码的优势。
二、更简洁的无重复代码方案:类装饰器
相比make_dataclass,用类装饰器自动添加属性的方式可读性更高,结合Protocol同样能让mypy识别类型:
from enum import Enum from dataclasses import dataclass from typing import Protocol class DatasetNames(Enum): test1 = "test1_string" test2 = "test2_string" test3 = "test3_string" def get_path(s: str) -> str: return s + "_path" def add_dataset_paths(cls): """自动添加数据集路径类属性的装饰器""" for name in DatasetNames: setattr(cls, name.name, get_path(name.value)) return cls # 定义Protocol提供类型信息 class DatasetPathsProtocol(Protocol): test1: str test2: str test3: str # 用装饰器生成类,同时让mypy通过Protocol识别属性类型 @dataclass(frozen=True) @add_dataset_paths class DynamicDatasetPaths(DatasetPathsProtocol): pass print(DynamicDatasetPaths.test2) # 'test2_string_path'
这种方式避免了手动重复编写属性,代码结构更清晰,mypy也能正确识别类属性的类型。
补充优化:简化Protocol定义
如果不想逐个列出属性,可以借助mypy的__getattr__魔法注解(仅对mypy生效),无需手动维护属性列表:
from typing import Literal class DatasetPathsProtocol(Protocol): @classmethod def __getattr__(cls, name: Literal["test1", "test2", "test3"]) -> str: ...
这样mypy会自动识别指定名称的属性为str类型。
内容的提问来源于stack exchange,提问作者Pro Q
相关产品推荐
相关产品推荐

