首页 > 解决方案 > 在 yaml 文件中分解映射键

问题描述

我有这个 YAML 文件:

pb:
  {EF:{16, 19}, EH:{16, 19}}

当我应用我的flattendictPython 函数时,我得到了这个

{('pb', 'EF', 16): None,
 ('pb', 'EF', 19): None,
 ('pb', 'EH', 16): None,
 ('pb', 'EH', 19): None}

我如下搜索我的 YAML 文件的语法,以获得相同的结果(我想考虑我的 YAML 节点数据)

pb:
  {EF, EH}, {16, 19}}

你有想法吗?

这是我的python flattendict 函数

#!/usr/bin/env python
#encoding: UTF-8
import codecs
import sys
import yaml
import pprint

import collections

from collections import Mapping
from itertools import chain
from operator import add

_FLAG_FIRST = object()

def flattenDict(d, join=add, lift=lambda x:x):
    results = []
    def visit(subdict, results, partialKey):
        for k,v in subdict.items():
            newKey = lift(k) if partialKey==_FLAG_FIRST else join(partialKey,lift(k))
            if isinstance(v,Mapping):
                visit(v, results, newKey)
            else:
                results.append((newKey,v))
    visit(d, results, _FLAG_FIRST)
    return results

testdata = yaml.safe_load(open('data.yaml', 'r')) 
from pprint import pprint as pp
result = flattenDict(testdata, lift=lambda x:(x,))
pp(dict(result))

标签: pythonyamlflattenpyyaml

解决方案


在 YAML 中,您可以有一个复杂的流节点,即使是一个简单的键(即没有?, 标记)。在YAML 1.2YAML 1.1中都是如此。这意味着:

{a: 1, b: 2}: mapping
[1, 2, a]: sequence

是正确的 YAML。

问题是映射通常作为 Python 加载,dict序列作为 Python加载list,两者都是可变的,不能被散列,并且不允许作为 Python 的键dict(尝试执行python -c "{{'a': 1}: 2}")。

PyYAML(支持 YAML 1.1)在这两行中都出现错误。

由于 Python 有一个list形式的不可变对象tuple,我决定在 Python 中通过将它们构造为元组来实现序列键的加载ruamel.yaml(它支持 YAML 1.2 和 YAML 1.1)。所以以下工作:

import sys
import ruamel.yaml
from pprint import pprint as pp

yaml_str = """\
[pb, EF, 16]: 
[pb, EF, 19]: 
[pb, EH, 16]: 
[pb, EH, 19]: 
"""


yaml = ruamel.yaml.YAML(typ='rt')
# yaml.indent(mapping=4, sequence=4, offset=2)
# yaml.preserve_quotes = True
data = yaml.load(yaml_str)

pp(data)
print('---------')
yaml.dump(data, sys.stdout)

印刷:

{('pb', 'EF', 16): None,
 ('pb', 'EF', 19): None,
 ('pb', 'EH', 16): None,
 ('pb', 'EH', 19): None}
---------
[pb, EF, 16]:
[pb, EF, 19]:
[pb, EH, 16]:
[pb, EH, 19]:

如果您尝试在 PyYAML 中加载上述 YAML,则会引发异常:

found unhashable key
  in "<unicode string>", line 1, column 1:
    [pb, EF, 16]: 

笔记:

  • 如果您不想往返,请使用typ="safe",它使用更快的 C 加载器,它也处理键即序列,但它不会巧妙地转储这些键,从而导致?标记显式键。

  • 一个针对 Python 的 a提案frozendict没有被接受,因此没有等效的,甚至在标准库中也没有 a dictwhat tupleis for a list,并且ruamel.yaml不支持作为开箱即用的键的映射。如果你有这样一个frozendict,你当然可以将它添加到ruamel.yaml的构造函数中。

  • 尽管frozensetPython 中有 a ,YAML中有一个 set ,但目前不ruamel.yaml接受以下输入:

    !!set {a , b}: value
    
  • 可能不用说:如果不删除并重新添加键值对,就无法以编程方式更改此类键的元素。


推荐阅读