Условный сброс атрибута с использованием pyyaml / ruamel.yaml

#python #yaml #pyyaml #python-dataclasses #ruamel.yaml

#python #yaml #pyyaml #python-классы данных #ruamel.yaml

Вопрос:

У меня есть класс Python примерно так:

 from dataclasses import dataclass
from ruamel.yaml import yaml_object, YAML

yaml = YAML()

@yaml_object(yaml)
@dataclass
class DataObject:
    normal_attr: str
    normal_attr_2: str
    conditional_attr: str
  

Затем я хочу иметь возможность условного сброса conditional_attr с использованием ruamel.yaml (который основан на PyYAML) на основе некоторого условия. В идеале это будет работать примерно так:

 data = DataObject()

if verbose_output:
    yaml.dump(data, stream)
else:
    yaml.dump(data, stream, exclude=['conditional_attr'])
  

Конечно, на самом деле это не работает, но есть ли какой-нибудь способ реализовать это поведение?

Ответ №1:

Я не думаю, что было бы хорошей идеей реализовать исключение определенных ключей сопоставления в качестве опции для метода дампа.

И, кроме того ruamel.yaml , уже есть документированный метод для создания специальных дампов, которые вы можете использовать для фильтрации любого атрибута, например, для тех, где имя начинается с conditional_ :

 import sys
from dataclasses import dataclass
from ruamel.yaml import yaml_object, YAML

yaml = YAML()

verbose_output = False

@yaml_object(yaml)
@dataclass
class DataObject:
    normal_attr: str
    normal_attr_2: str
    conditional_attr: str

    @classmethod
    def to_yaml(cls, representer, node):
        d = {}
        for k in node.__annotations__:
            if not verbose_output and k.startswith('conditional_attr'):
                continue
            d[k] = node.__getattribute__(k)
        return representer.represent_mapping('!DataObject', d)

data = DataObject(normal_attr='a', normal_attr_2='b', conditional_attr='c')

yaml.dump(data, sys.stdout)
  

это дает:

 !DataObject
normal_attr: a
normal_attr_2: b
  

Комментарии:

1. Я думаю, было бы идеально указывать поля в момент сериализации. Это то, что делает Marshmallow: marshmallow.readthedocs.io/en/stable /…

2. В любом случае, этот ответ — это начало, но verbose_output здесь это глобальная переменная, которая на самом деле не решает мою проблему, потому что вы не можете разумно создать повторно используемый модуль, который позволяет пользователю настраивать детализацию.

3. verbose_output это глобальная переменная в вашем коде, поэтому я продолжил ее использование здесь. Если у вас есть одна процедура сериализации, добавление аргументов может быть в порядке, но ruamel . yaml (и PyYAML) имеют несколько сериализаций, которые все нуждаются в обновлении с каждым новым параметром, который кто-то придумывает. ИМО гораздо эффективнее сделать это объектно-ориентированным способом, который может использовать каждый сериализатор.

4. Если бы мне нужно было что-то подобное, я бы не использовал глобальную переменную, как вы, а добавил атрибут yaml.verbose_dataclass_output = False . И создайте альтернативный декоратор для yaml_object этого, который по-прежнему принимает экземпляр YAML в качестве параметра, но добавляет метод to_yaml classmethod (который проверяет verbose_data_class атрибут) к каждому классу, который он украшает.