Дамп ruamel.yaml python классов, содержащих свойства, кажется, приводит к неожиданному форматированию yaml - PullRequest
1 голос
/ 08 января 2020

Я работаю над дампом данных класса 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'

Есть ли способ преодолеть это без написания пользовательских конструкторов и представителей для каждого класса с нуля?

1 Ответ

0 голосов
/ 08 января 2020

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'
...