Как загрузить файл pyYAML и получить к нему доступ с помощью атрибутов вместо использования нотации словаря?

У меня есть конфигурация YAML, которая выглядит так:

config:
 - id: foo
 - name: bar
content:
 - run: xxx
 - remove: yyy

Я использую модуль Python YAML для его загрузки, но я хочу получить к нему доступ лучше, например:

stream = open(filename)
config = load(stream, Loader=Loader)
print(config['content'])

Я хочу иметь возможность делать: print(config.content).


person sorin    schedule 15.06.2012    source источник
comment
Дубликат stackoverflow.com/questions/2352181/   -  person Justin    schedule 15.06.2012
comment
@Justin: Это не дубликат этого вопроса, потому что вы можете просто исправить загрузчик YAML, чтобы создавать объекты любого желаемого класса, а не экземпляры dict.   -  person Dietrich Epp    schedule 15.06.2012


Ответы (2)


Вы можете использовать обозначение объектов со словарями, используя следующий класс, как описано в этом ответе:

class DictAsMember(dict):
    def __getattr__(self, name):
        value = self[name]
        if isinstance(value, dict):
            value = DictAsMember(value)
        return value

Этот класс в действии:

>>> my_dict = DictAsMember(one=1, two=2)
>>> my_dict
{'two': 2, 'one': 1}
>>> my_dict.two
2

Редактировать Это работает рекурсивно с подсловарями, например:

>>> my_dict = DictAsMember(one=1, two=2, subdict=dict(three=3, four=4))
>>> my_dict.one
1
>>> my_dict.subdict
{'four': 4, 'three': 3}
>>> my_dict.subdict.four
4
person Chris    schedule 15.06.2012
comment
Я думаю, что это не работает рекурсивно, так как вы можете себе представить, что та же проблема повторяется для других записей в дереве конфигурации. - person sorin; 15.06.2012
comment
Если я понимаю, что вы имеете в виду, это работает рекурсивно. Мое редактирование отвечает вашей точке зрения? - person Chris; 15.06.2012
comment
Почему за этот (самый простой ответ) не проголосовали миллион раз? Есть десятки похожих/повторяющихся вопросов, которые отвечают на это таким сложным образом! - person brandonscript; 19.01.2018

Самый простой способ сделать это, вероятно, перезаписать конструктор YAML для tag:yaml.org,2002:map, чтобы он возвращал пользовательский класс словаря вместо обычного словаря.

import yaml

class AttrDict(object):
    def __init__(self, attr):
        self._attr = attr
    def __getattr__(self, attr):
        try:
            return self._attr[attr]
        except KeyError:
            raise AttributeError

def construct_map(self, node):
    # WARNING: This is copy/pasted without understanding!
    d = {}
    yield AttrDict(d)
    d.update(self.construct_mapping(node))

# WARNING: We are monkey patching PyYAML, and this will affect other clients!    
yaml.add_constructor('tag:yaml.org,2002:map', construct_map)

YAML = """
config:
  - id: foo
  - name: bar
content:
  - run: xxx
  - remove: yyy
"""

obj = yaml.load(YAML)

print(obj.config[0].id) # prints foo

Обратите внимание, что это нарушит все остальное в процессе, который использует YAML, если он ожидает, что все будет работать обычным способом Python. Вы можете использовать собственный загрузчик, но я лично нахожу документацию PyYAML немного запутанной, и кажется, что побочные эффекты являются глобальными и заразными, как правило, а не исключение.

Вы были предупреждены.

В качестве альтернативы, если ваша схема относительно статична, вы можете написать свои собственные классы и десериализовать их (например, class Config со свойствами id и name). Однако, вероятно, это не стоило бы затрат на дополнительный код.

person Dietrich Epp    schedule 15.06.2012
comment
Упрощен и улучшен до gist.github.com/ktaragorn/9cf6d368378b0f65a3a0. Там нет обезьяньего патча, и он также работает во вложенном режиме. - person Karthik T; 26.10.2015
comment
@KarthikT: не работает для словарей, вложенных в массивы. - person Dietrich Epp; 26.10.2015
comment
Я даже не рассматривал этот вариант использования ... часто ли это происходит, когда вы рассматриваете файлы конфигурации? - person Karthik T; 26.10.2015
comment
Да, я думаю, это будет часто. Например, при настройке веб-сервера у вас будет список различных виртуальных хостов. - person Dietrich Epp; 26.10.2015