I have two dataclasses: Msg
and Field
. Msg has a field fields
of type list[Field]
. I want to assign something to a field of each Field
after they have all been initialized which is more or less their relative index in the fields
list.
However, when I add a __post_init__(self)
method to the Msg
dataclass, the fields
list is empty, so I can't update the indices.
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 :(
Why is this? How is the Msg
being initialized without fields
being initialized? Is there any way to do what I am trying to do using the class system? I am using Python 3.11 with ruamel.yaml 0.18.5 on MacOS.
By default, object serializers such as YAML and pickle
have no idea what to do with the attribute mapping for a user-defined object other than to assign the mapping directly to the object's attribute dictionary as-is.
This is why you can define a __setstate__
method for your class, so that ruamel.yaml
's object constructor knows in this case to call the __init__
method with the mapping unpacked as arguments, which in turn calls __post_init__
for post-initialization:
@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)