如何加载pyYAML文件并使用属性而不是使用字典表示法来访问它?

时间:2012-06-15 10:38:41

标签: python yaml 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)

2 个答案:

答案 0 :(得分:8)

您可以使用以下类在字典中使用对象表示法,如this中所述:

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

答案 1 :(得分:4)

最简单的方法是覆盖tag:yaml.org,2002:map的YAML构造函数,以便返回自定义字典类而不是普通字典。

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

请注意,如果它希望一切都以普通的Python方式工作,那么这将破坏使用YAML的进程中的其他所有内容。你可以使用一个自定义加载器,但我个人觉得PyYAML文档有点迷宫,而且看起来副作用是全局的,而且是传染性的,而不是例外。

你被警告了。

作为替代方案,如果您的架构是相对静态的,您可以编写自己的类并反序列化(例如,class Config idname属性)。但是,它可能不值得额外代码的成本。