如何基于Pydantic动态生成类以反序列化Rust风格枚举
实现Pydantic版Rust风格标记联合
实现思路
利用Pydantic的__init_subclass__动态生成适配字段,结合全局字段验证器强制“仅能设置一个变体”的约束,让子类只需定义嵌套的BaseModel变体,即可自动适配Rust枚举的序列化格式。
完整实现代码
from pydantic import BaseModel, field_validator, ValidationError from typing import Optional, Type class RustEnum(BaseModel): @classmethod def __init_subclass__(cls): super().__init_subclass__() # 收集子类中定义的所有BaseModel类型变体 variants = {} for name, attr in list(cls.__annotations__.items()): # 处理Optional包裹的变体类型 if getattr(attr, '__origin__', None) is Optional: attr = attr.__args__[0] if isinstance(attr, Type) and issubclass(attr, BaseModel): variants[name] = attr # 移除原大写命名的注解,避免字段冲突 del cls.__annotations__[name] # 动态添加小写命名的可选字段(对应Rust序列化的键名) for variant_name, variant_model in variants.items(): field_name = variant_name.lower() cls.__annotations__[field_name] = Optional[variant_model] setattr(cls, field_name, None) @field_validator('*', mode='after') def enforce_single_variant(cls, values): # 统计已设置的变体数量 active_variants = [key for key, value in values.items() if value is not None] if len(active_variants) != 1: raise ValueError(f"必须且仅能设置一个变体,当前已设置{len(active_variants)}个:{active_variants}") return values
使用示例
定义变体与枚举类
# 对应Rust枚举的变体模型 class Foo(BaseModel): a: int class Bar(BaseModel): b: str # 继承RustEnum创建枚举类 class FooBar(RustEnum): Foo: Foo Bar: Bar
验证正确场景
# 实例化Foo变体 foo_obj = FooBar(foo={"a": 123}) print(foo_obj.model_dump()) # 输出: {'foo': {'a': 123}} # 实例化Bar变体 bar_obj = FooBar(bar={"b": "hello"}) print(bar_obj.model_dump()) # 输出: {'bar': {'b': 'hello'}}
验证错误场景
# 同时设置多个变体,触发验证错误 try: FooBar(foo={"a": 123}, bar={"b": "hello"}) except ValidationError as e: print(e.errors()[0]['msg']) # 输出: 必须且仅能设置一个变体,当前已设置2个:['foo', 'bar'] # 未设置任何变体,触发验证错误 try: FooBar() except ValidationError as e: print(e.errors()[0]['msg']) # 输出: 必须且仅能设置一个变体,当前已设置0个:[]
关键细节说明
__init_subclass__:在子类继承时自动扫描BaseModel类型的变体定义,将大写变体名转换为小写可选字段,完美匹配Rust枚举的序列化键名格式。- 全局字段验证器:通过
field_validator('*', mode='after')在所有字段验证完成后,检查变体设置数量,严格保证“仅一个变体”的约束。 - 低侵入式定义:子类只需声明变体模型,无需额外编写字段或验证逻辑,批量创建枚举类时效率极高。
内容的提问来源于stack exchange,提问作者JP-Ellis
相关产品推荐
相关产品推荐

