Python中继承frozen dataclass的子类设置常量Enum字段的更优方案
最优实现方案(基于抽象基类,无冗余、强类型安全)
from enum import Enum from dataclasses import dataclass from abc import ABC, abstractmethod class MessageType(Enum): HELLO = 10 BYE = 20 @dataclass(frozen=True) class Message(ABC): # 抽象属性约束所有子类必须实现op字段 @property @abstractmethod def op(self) -> MessageType: ... # 子类仅需声明一次固定op值,无任何冗余 @dataclass(frozen=True) class Hello(Message): op = MessageType.HELLO foo: str
该方案核心优势:
- 完全消除代码冗余:每个消息子类仅需指定一次op的固定值,不需要重复声明
- 语法层面禁止自定义op:实例化子类时不需要、也无法传入op参数,同时frozen dataclass天生禁止修改实例的op属性
- 完美适配类型校验:mypy、pyright等类型检查器可自动识别
Hello.op的类型为固定值Literal[MessageType.HELLO],你要求的处理函数类型校验能力完全保留,例如def process_hello(msg: Hello): msg.foo + 123会正常触发类型错误。
如果需要批量反序列化消息,可以搭配自动映射实现:
# 自动构建op到子类的映射表 op_mapping = {subcls.op: subcls for subcls in Message.__subclasses__()} # 反序列化示例,收到原始数据后自动转为对应子类实例 def deserialize(raw_op: int, **kwargs) -> Message: op_enum = MessageType(raw_op) return op_mapping[op_enum](**kwargs)
备选方案(基于类装饰器,无需修改父类)
如果不想引入抽象基类、需要兼容现有父类定义,可以用装饰器方案:
from enum import Enum from dataclasses import dataclass, field class MessageType(Enum): HELLO = 10 @dataclass(frozen=True) class Message: op: MessageType # 定义装饰器自动注入固定op字段 def fixed_op(op_val: MessageType): def wrapper(cls): cls.op = field(default=op_val, init=False) cls.__annotations__["op"] = type(op_val) return dataclass(frozen=True)(cls) return wrapper # 子类定义无冗余 @fixed_op(MessageType.HELLO) class Hello(Message): foo: str
内容的提问来源于stack exchange,提问作者knite
相关产品推荐
相关产品推荐

