【问题标题】:How to load a pyYAML file and access it using attributes instead of using the dictionary notation?如何加载 pyYAML 文件并使用属性而不是使用字典表示法访问它?
【发布时间】:2012-06-18 10:21:45
【问题描述】:

我有一个 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)

【问题讨论】:

  • @Justin:这不是这个问题的重复,因为你可以简单地修补 YAML 加载器来创建你想要的任何类的对象,而不是 dict 的实例。

标签: python yaml pyyaml


【解决方案1】:

执行此操作的最简单方法可能是覆盖 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 Configidname 属性)。但是,这可能不值得花费额外的代码。

【讨论】:

  • 将其简化并改进为gist.github.com/ktaragorn/9cf6d368378b0f65a3a0。没有猴子补丁,它也以嵌套方式工作
  • @KarthikT:嵌套在数组中的字典失败。
  • 我什至没有考虑过那个用例..当您考虑配置文件时,这种情况经常发生吗?
  • 是的,我认为它会经常发生。例如,在配置 Web 服务器时,您会有一个不同虚拟主机的列表。
【解决方案2】:

您可以使用以下类将对象表示法与字典一起使用,如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

【讨论】:

  • 我认为这不能递归地工作,因为您可以想象配置树中的其他条目会重复同样的问题。
  • 如果我明白你的意思,这确实是递归的。我的编辑是否回答了您的观点?
  • 为什么这个(最简单的答案)没有被投票一百万次?有几十个类似/重复的问题以如此复杂的方式回答这个问题!
猜你喜欢
  • 2012-04-04
  • 2021-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-06
  • 2013-08-07
  • 1970-01-01
  • 2011-08-26
相关资源
最近更新 更多