首页 > 解决方案 > 包含属性的 python 类的 ruamel.yaml 转储似乎会产生意外的 yaml 格式

问题描述

我正在将 python 类数据转储到 YAML,以便稍后加载。我发现使用 ruamel.yaml(和 PyYAML),当我使用属性转储类来管理类属性时,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'

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

标签: pythonruamel.yaml

解决方案


ruamel.yaml不将属性/设置器作为“普通”属性处理,这就是为什么你会得到有趣的输出,这有效的 YAML。

首先,尽管您应该更改您的 attribute1 属性,因为调用print(sample1.attribute1)将使您进入无限递归。

然后,您可以只创建一个基类,该基类具有to_yaml可用于转储所有类的适当方法:

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'

推荐阅读