You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让ruamel.yaml加载YAML时调用类的__post_init__方法?

解决ruamel.yaml加载dataclass时触发__post_init__的问题

问题描述

原本用ruamel.yaml从YAML文件构建dataclass对象运行正常,但尝试通过__post_init__方法随机选择可选值时,该方法未被调用。原因是ruamel.yaml默认的反序列化机制不会调用类的构造器,而是直接创建空实例并设置属性,导致依赖__init__触发的__post_init__无法执行。

用户提供的代码及配置如下:

Python代码

import random

from abc import ABC, abstractmethod
from dataclasses import dataclass
from ruamel.yaml import YAML, yaml_object

yaml = YAML()

@dataclass
class A(ABC):
    some_var: str
    options: list[str]

    def __post_init__(self):
        self.other_var = random.choice(self.options)

@yaml_object(yaml)
@dataclass
class B(A):
    yaml_tag = "!B"
    more: int

@yaml_object(yaml)
@dataclass
class C(A):
    yaml_tag = "!C"
    foo: list[int]

    def __post_init__(self):
        super().__post_init__()
        self.bar = random.choice(self.foo)

# …
data = yaml.load("config.yml")

YAML配置(config.yml)

test_classes:
  - !B
    some_var: abc123
    options: ['X', 'Y', 'Z']
    more: 7
  - !C
    some_var: abc123
    options: ['X', 'Y', 'Z']
    foo: [7, 12, 42]

解决方案

要触发__post_init__,需让ruamel.yaml调用类的构造器实例化对象。为每个标记类添加自定义的from_yaml类方法,手动调用构造器完成实例化即可。

修改后的代码如下:

import random

from abc import ABC, abstractmethod
from dataclasses import dataclass
from ruamel.yaml import YAML, yaml_object

yaml = YAML()

@dataclass
class A(ABC):
    some_var: str
    options: list[str]

    def __post_init__(self):
        self.other_var = random.choice(self.options)

@yaml_object(yaml)
@dataclass
class B(A):
    yaml_tag = "!B"
    more: int

    @classmethod
    def from_yaml(cls, constructor, node):
        # 解析YAML节点为属性字典
        mapping = constructor.construct_mapping(node, deep=True)
        # 通过构造器创建实例,自动触发__init__和__post_init__
        return cls(**mapping)

@yaml_object(yaml)
@dataclass
class C(A):
    yaml_tag = "!C"
    foo: list[int]

    def __post_init__(self):
        super().__post_init__()
        self.bar = random.choice(self.foo)

    @classmethod
    def from_yaml(cls, constructor, node):
        mapping = constructor.construct_mapping(node, deep=True)
        return cls(**mapping)

# 测试加载
data = yaml.load("config.yml")
# 验证结果
for item in data['test_classes']:
    print(type(item).__name__, item.other_var)
    if isinstance(item, C):
        print("C类bar值:", item.bar)

原理说明

ruamel.yaml默认处理自定义标签对象时,会用object.__new__(cls)创建空实例(跳过__init__),再逐个赋值属性。自定义from_yaml方法后,会先解析YAML节点为键值对字典,再通过cls(**mapping)调用dataclass自动生成的构造器,从而触发__init__及后续的__post_init__方法,完成随机值的初始化逻辑。

内容的提问来源于stack exchange,提问作者Darkproduct

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 13:53:11