小能豆

使用 ruamel.yaml 调用 __post_init__() 时未初始化字段

python

我有两个数据类:MsgField。Msg 有一个fields类型字段list[Field]。我想在它们全部初始化后为每个字段分配一些内容,Field这或多或少是它们在列表中的相对索引fields

但是,当我__post_init__(self)Msg数据类添加方法时,fields列表为空,因此我无法更新索引。

from dataclasses import dataclass
from ruamel.yaml import YAML

@dataclass
class Msg:

    id: int
    desc: str
    fields: list[Field]

    def __post_init__(self) -> None:
        idx: int = 0
        for field in self.fields: # why is this empty??
            field.index = idx
            idx += field.size

@dataclass
class Field:
    id: int
    name: str
    units: str
    size: int
    index: int = -1

y = YAML()
y.register_class(Msg)
y.register_class(Field)

msg: Msg = y.load("""\
!Msg
id: 1
desc: status
fields:
- !Field
    id: 1
    name: Temp
    units: degC
    size: 2
""")

assert(msg.fields[0].index != -1) # fails :(

为什么是这样?Msg未初始化的情况下如何初始化fields?有什么办法可以使用班级系统来做我想做的事情吗?我在 MacOS 上使用 Python 3.11 和 ruamel.yaml 0.18.5。


阅读 82

收藏
2023-11-15

共1个答案

小能豆

默认情况下,对象序列化程序(例如 YAML)pickle不知道如何处理用户定义对象的属性映射,除了按原样将映射直接分配给对象的属性字典之外。

这就是为什么您可以__setstate__为您的类定义一个方法,以便ruamel.yaml在这种情况下,对象构造函数知道使用__init__解包为参数的映射来调用该方法,这反过来又调用__post_init__后初始化:

@dataclass
class Msg:
    id: int
    desc: str
    fields: list[Field]

    def __post_init__(self) -> None:
        idx: int = 0
        for field in self.fields:
            field.index = idx
            idx += field.size

    def __setstate__(self, state):
        self.__init__(**state)
2023-11-15