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

Posted

技术标签:

【中文标题】如何加载 pyYAML 文件并使用属性而不是使用字典表示法访问它?【英文标题】:How to load a pyYAML file and access it using attributes instead of using the dictionary notation? 【发布时间】: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)

【问题讨论】:

***.com/questions/2352181/… 的副本 @Justin:这不是这个问题的重复,因为你可以简单地修补 YAML 加载器来创建你想要的任何类的对象,而不是 dict 的实例。 【参考方案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

【讨论】:

我认为这不能递归地工作,因为您可以想象配置树中的其他条目会重复同样的问题。 如果我明白你的意思,这确实是递归的。我的编辑是否回答了您的观点? 为什么这个(最简单的答案)没有被投票一百万次?有几十个类似/重复的问题以如此复杂的方式回答这个问题!

以上是关于如何加载 pyYAML 文件并使用属性而不是使用字典表示法访问它?的主要内容,如果未能解决你的问题,请参考以下文章

我可以在 yaml/pyyaml 中转储空白而不是 null 吗?

如何使用 PyYAML 重命名密钥

PyYAML 和不寻常的标签

PyYAML 转储格式

pyyaml 加载数字为十进制

如何使用 QTextStream 而不是 QDataStream 从 QTableView 进行加载保存?