pyyaml 将字典映射到对象字典

pyyaml map dict to dict of objects

我正在努力使用 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 个节点会更好

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

进行调整

PyYAML 中没有内置的选项,即没有内置的方式告诉加载程序 lut 让 PyYAML 用它做一些事情,当然不附加键值对(假设这就是您对节点的意思)作为其键的值。

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

如果你想尝试用 add_constructor 做这个,那么你需要做的是用 __call__ 方法构造一个 class,创建一个 class 以 lut 作为参数,然后将该实例作为替代构造函数传入):

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)

其中你可以用u'tag:yaml.org,2002:map'替换'your_tag' 你的构造函数来处理(所有)普通的指令。

另一种选择是在 YAML 加载期间执行此操作,但您不能再一次像通常提交 class 不是对象。您需要一个对象才能附加 lut。所以你要做的是创建你自己的构造函数和你自己的使用该构造函数的加载器,然后 load() 替换实例化你的加载器,附加 lut (通过将它添加为唯一属性,或者通过将其作为参数传入并将其传递给您的构造函数)。

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

我肯定会首先使用两个例程实现 post 流程阶段:

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}  

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

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