使用ruamel.yaml序列化带__getstate__/__setstate__的类时保留锚点名称
解决ruamel.yaml中yaml_object锚点丢失且保留状态控制的问题
问题背景
使用ruamel.yaml 0.17.21的yaml_object装饰器实现自定义类的序列化与反序列化时,为了避免临时修改被自动保存到YAML,通过__getstate__和__setstate__缓存初始状态,仅允许修改_permanent_state来实现永久变更。但此时YAML中的锚点名称(如&simple)会被替换成&id001这类自动生成的ID;移除这两个方法后锚点名称能保留,但状态控制逻辑失效。
解决方案
在自定义类中实现__yaml_anchor__方法,手动保存加载时的锚点信息,让ruamel.yaml序列化时复用原始锚点名称,同时保留__getstate__/__setstate__的状态控制逻辑。
修改后的完整代码
import sys from copy import deepcopy from ruamel.yaml import YAML, yaml_object yaml = YAML() @yaml_object(yaml) class ExampleClass(): def __init__(self): self._permanent_state = {} self._yaml_anchor = None # 存储加载时的锚点信息 def __setstate__(self, state): # 加载时保存锚点信息(如果存在) if hasattr(state, '_yaml_anchor'): self._yaml_anchor = state._yaml_anchor # 恢复永久状态并初始化临时状态 self.__dict__.update(deepcopy(state)) self._permanent_state = state def __getstate__(self): # 仅返回永久状态用于序列化 return self._permanent_state def __yaml_anchor__(self): # 返回自定义锚点,无则使用默认生成的 return self._yaml_anchor source = """ simple: &simple !ExampleClass potato: 10 turnip: 20 nested: !ExampleClass sunflower: 30 others: *simple """ a = yaml.load(source) a['simple'].potato = 10**6 # 临时修改,不会被序列化 a['nested']._permanent_state['sunflower'] = 100 # 永久修改,会被序列化 yaml.dump(a, sys.stdout)
运行输出
simple: &simple !ExampleClass potato: 10 turnip: 20 nested: !ExampleClass sunflower: 100 others: *simple
原理说明
__yaml_anchor__是ruamel.yaml提供的钩子方法,用于自定义对象的锚点处理。当YAML加载时,ruamel.yaml会为带锚点的对象自动设置锚点属性,我们通过__setstate__将其保存到实例的_yaml_anchor变量中。- 序列化时,ruamel.yaml会调用
__yaml_anchor__获取锚点信息,从而使用原始的锚点名称(如simple),而非自动生成的id001。 __getstate__和__setstate__依然发挥作用:__getstate__仅返回_permanent_state用于序列化,确保临时修改不会被保存;__setstate__负责初始化实例状态并保存锚点。
内容的提问来源于stack exchange,提问作者JeanOlivier
相关产品推荐
相关产品推荐

