如何用Pythonic面向对象方案实现多类型机器人消息的类设计
Python实现类Haskell和类型消息的最优方案
你需要的是Python生态下对应Haskell代数和类型(Sum Type)的实现,用「数据类+基类+模式匹配/单分发」的组合方案即可完美解决你提到的两个原有方案的缺陷:
核心实现代码
首先利用Python 3.7+内置的dataclasses模块消除模板代码,每个消息类型仅需定义自身字段即可:
from dataclasses import dataclass from typing import Union # 公共基类,统一所有消息的父类型 class Message: pass # 各消息类型定义,和Haskell构造器一一对应,无冗余模板代码 @dataclass(frozen=True) # 加frozen可实现不可变,更接近Haskell的代数类型特性 class Greeting(Message): foo: str bar: int @dataclass(frozen=True) class Warning(Message): yadda_1: str yadda_2: float yadda_3: bool # 统一的消息类型标注,完全对齐Haskell的sum type定义 MessageUnion = Union[Greeting, Warning]
消息处理逻辑
Python 3.10+ 推荐用模式匹配,完全对齐Haskell的模式匹配写法:
def handle_message(msg: MessageUnion) -> None: match msg: case Greeting(foo=f, bar=b): # 处理问候消息 print(f"Greeting: {f}, {b}") case Warning(yadda_1=y1, yadda_2=y2, yadda_3=y3): # 处理警告消息 print(f"Warning: {y1}, {y2}, {y3}") case _: raise ValueError(f"未知消息类型: {type(msg)}")
低版本Python可用单分发实现:
from functools import singledispatch @singledispatch def handle_message(msg: MessageUnion) -> None: raise ValueError(f"未知消息类型: {type(msg)}") @handle_message.register def _(msg: Greeting) -> None: print(f"Greeting: {msg.foo}, {msg.bar}") @handle_message.register def _(msg: Warning) -> None: print(f"Warning: {msg.yadda_1}, {msg.yadda_2}, {msg.yadda_3}")
方案优势
- 无冗余模板代码:
dataclass自动生成构造器、__repr__、__eq__等通用方法,每个消息类型的定义和Haskell的构造器一样简洁 - 类型安全:所有字段的类型清晰可查,mypy等静态检查工具可以完美校验,不存在属性不可预测的问题
- 逻辑对齐Haskell实现:所有消息同属
Message基类,和Haskell中所有构造器同属一个类型的设计完全对应 - 符合Python最佳实践:没有自定义黑魔法,生态工具兼容性好,可读性高
内容的提问来源于stack exchange,提问作者impresso
相关产品推荐
相关产品推荐

