ruamel.yaml python 类 包含属性的转储似乎会产生意外的 yaml 格式

ruamel.yaml dump of python classes containing properties seem to produce unexpected yaml formatting

我正在努力将 python class 数据转储到 YAML,以便稍后加载。我发现使用 ruamel.yaml(和 PyYAML)时,当我使用 属性 转储 class 来管理 class 属性时,YAML 输出更改为似乎无效的内容YAML 语法。我将下面的代码放在一起来演示行为

import sys
import ruamel.yaml

class MySampleClass(object):
    def __init__(self, attribute1, attribute2):
        self.attribute1 = attribute1
        self.attribute2 = attribute2

    @property
    def attribute1(self):
        return self.attribute1

    @attribute1.setter
    def attribute1(self, attribute1):
        self.__attribute1 = attribute1


sample1 = MySampleClass("ABCD", "123")
yaml = ruamel.yaml.YAML()
yaml.register_class(MySampleClass)
yaml.dump(sample1, sys.stdout)

当 运行 这会产生下面的输出。如您所见,装饰的第一个 属性 具有意外的格式,而第二个是人们对 YAML 的期望。

!MySampleClass
_MySampleClass__attribute1: ABCD
attribute2: '1234'

有没有办法在不从头开始为每个 class 编写自定义构造函数和表示器的情况下克服这个问题?

ruamel.yaml 不将 properties/setters 处理为 "normal" 属性,这就是为什么你得到有趣的输出, 有效的 YAML .

首先你应该改变你的属性 1 属性,因为调用 print(sample1.attribute1) 会 让你进入无限递归。

然后你可以只创建一个具有适当 to_yaml 方法的基础 class 可用于转储所有 classes:

import sys
import ruamel.yaml
yaml = ruamel.yaml.YAML()

class ObjWithProperties(object):
    @classmethod
    def to_yaml(cls, representer, node):
        tag = getattr(cls, 'yaml_tag', '!' + cls.__name__)
        attribs = {}
        for x in dir(node):
            if x.startswith('_'):
                continue
            v = getattr(node, x)
            if callable(v):
                continue            
            attribs[x] = v
        return representer.represent_mapping(tag, attribs)


@yaml.register_class
class MySampleClass(ObjWithProperties):
    def __init__(self, attribute1, attribute2):
        self.attribute1 = attribute1
        self.attribute2 = attribute2

    @property
    def attribute1(self):
        return self.__attribute1  # <<<< note the added double underscore to prevent recursion

    @attribute1.setter
    def attribute1(self, attribute1):
        self.__attribute1 = attribute1


sample1 = MySampleClass("ABCD", "123")
yaml.dump(sample1, sys.stdout)

给出:

!MySampleClass
attribute1: ABCD
attribute2: '123'