【问题标题】:pyyaml map dict to dict of objectspyyaml 将字典映射到对象的字典
【发布时间】:2016-04-30 02:27:17
【问题描述】:

我正在努力使用 PyYAML 文档来理解一件可能很容易的事情。 我有一个将字符串名称映射到 python 对象的字典:

lut = { 'bar_one': my_bar_one_obj,
        'bar_two': my_bar_two_obj }

我想像这样加载一个 YAML 文件并将所有“foo”节点映射到我的字典对象(相反,转储,并不是真正必要的)

node1:
  # ...
  foo: "bar_one"
node2:
  # ...
  foo: "bar_two"

我的第一个想法是使用add_constructor,但我找不到给它额外的 kwarg 的方法。也许是自定义加载器? PyYAML 文档不是很有帮助,或者我可能正在寻找错误的关键字...

我可以接受使用自定义标签,例如

node1:
  # ...
  foo: !footag "bar_one"
node2:
  # ...
  foo: !footag "bar_two"

但只检测foo 节点会更好

【问题讨论】:

    标签: python pyyaml


    【解决方案1】:

    您不是在寻找错误的关键字,这不是我所知道的任何 YAML 解析器要做的事情。 YAML 解析器加载一个可能很复杂的自包含数据结构。您要做的是在其中一个解析步骤中将该自包含结构合并到一个已经存在的结构中(lut)。解析器旨在通过提供替代例程而不是通过提供例程+数据

    来进行调整

    PyYAML 中没有内置的选项,即没有内置的方法告诉加载器lut 使 PyYAML 对其进行处理,当然也不会附加键值对(假设是你对节点的意思)作为其键的值。

    获得所需内容的最简单方法可能是使用一些后期处理,该处理采用 lut 和从 YAML 文件(也是一个字典)加载的数据并将两者结合起来。

    如果您想尝试使用add_constructor 执行此操作,那么您需要做的是使用__call__ 方法构造一个类,使用lut 作为参数创建该类的实例,然后传递该实例in 作为替代构造函数):

    class ConstructorWithLut:
        def __init__(self, lut):
            self._lut = lut
    
        def __call__(self):
            # the actual constructor routine added by add_constructor
    
    constructor_with_lut(lut)
    SomeConstructor.add_constructor('your_tag', constructor_with_lut)
    

    如果需要,您可以在其中将 'your_tag' 替换为 u'tag:yaml.org,2002:map' 您的构造函数来处理(所有)普通字典。

    另一种选择是在 YAML 加载期间执行此操作,但您不能再一次调整 Loader 或其组成组件之一(Constructor),因为您通常提交类而不是对象。您需要一个能够附加lut 的对象。因此,您要做的是创建自己的构造函数和使用该构造函数的加载器,然后使用 load() 替换来实例化加载器,附加 lut(只需将其添加为唯一属性,或将其传入作为参数并将其传递给您的构造函数)。

    您的构造函数应该是现有构造函数之一的子类,然后必须有自己的construct_mapping(),它首先调用父类'construct_mapping(),并且在返回结果之前,检查它是否可以更新它已分配lut 的属性。您不能基于查看foo 的字典键来执行此操作,因为如果您有这样的键,则您无权访问需要分配给@987654338 的父节点@。您需要做的是查看映射的任何值是否是具有键名foo 的字典,如果是,则可以使用字典根据与foo 关联的值更新lut .

    我当然会首先使用两个例程来实现后期处理阶段:

    def update_from_yaml(your_dict, yaml_data):
        for node_key in yaml_data:
            node_value = yaml_data[node_key]
            map_value(your_dict, node_key, node_value)
    
    def map_value(your_dict, key, value):
        foo_val = value.get('foo')
        if foo_val is None:  # key foo not found
            return
        your_dict[foo_val] = value  #  or  = {key: value}  
    

    我不确定“分配所有 foo 节点”的真正含义,YAML 数据在顶层没有节点,它只有键和值。所以你要么分配那对,要么只分配它的值(一个字典)。

    一旦这两个例程工作令人满意,您可以尝试实现基于add_constructorLoader 的替代方案,其中您至少应该能够重复使用map_value

    【讨论】:

    • 你可能是对的,这更适合后期处理程序。问题是“foo:object_name”对可以嵌套在任意级别的字典和列表中,所以我必须递归搜索和更新 yaml 字典。当我已经在做 yaml 解析时,重新实现递归 walker 似乎很愚蠢。
    • YAML 的问题是所有的例程都希望所有的状态都包含在数据中。如果您想根据环境中的某些本地动态状态做出解析决策怎么办?我想这不应该发生,因为解析应该是可重现的。
    • 是的,您对自包含性是正确的,但正如我所展示的,node1 值需要了解作为其值的 dict。我认为您最好的选择是 Loader 替代方案。我可以在 ruamel.yaml 中向您展示如何做到这一点,但我不确定它在 PyYAML 中的转化效果如何
    • 谢谢,我想我找到了一个解决方案,将 Loader 子类化以在 init 中获取 lut 并添加一个执行更新的自定义构造函数。
    猜你喜欢
    • 2015-10-17
    • 2011-06-24
    • 1970-01-01
    • 2016-11-22
    • 2020-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多