yaml使用键或父键作为值

yaml使用键或父键作为值,yaml,pyyaml,Yaml,Pyyaml,我刚刚开始使用YAML(通过pyyaml),我想知道是否有任何方法可以声明键的值是键名本身还是父键。 比如说 --- foo: &FOO bar: !. baz: !.. foo2: <<: *FOO … {‘foo’: {‘bar’: ‘bar’, ‘baz’: ’foo’}, ‘foo2’:{‘bar’:’bar’, ‘baz’:’foo2’}} 但是Node不持有密钥(或父节点的引用),我无法找到获取它们的方法 编辑: 下面是我的真实用例和基于Anth

我刚刚开始使用YAML(通过pyyaml),我想知道是否有任何方法可以声明键的值是键名本身还是父键。 比如说

---
foo: &FOO
  bar: !.
  baz: !..

foo2:
  <<: *FOO
…

{‘foo’: {‘bar’: ‘bar’, ‘baz’: ’foo’}, ‘foo2’:{‘bar’:’bar’, ‘baz’:’foo2’}}
但是Node不持有密钥(或父节点的引用),我无法找到获取它们的方法

编辑:

下面是我的真实用例和基于Anthon回应的解决方案: 我有一个记录器配置文件(在yaml中),我想重用其中的一些定义:

handlers:
    base: &base_handler
        (): globals.TimedRotatingFileHandlerFactory
        name: ../
        when: midnight
        backupCount: 14

        level: DEBUG
        formatter: generic

    syslog:
        class: logging.handlers.SysLogHandler
        address: ['localhost', 514]
        facility: local5

        level: NOTSET
        formatter: syslog

    access:
        <<: *base_handler

    error:
        <<: *base_handler

loggers:
    base: &base_logger
        handlers: [../, syslog]
        qualname: ../

    access:
        <<: *base_logger

    error:
        <<: *base_logger
        handlers: [../, syslog, email]

在构造元素时,您没有太多的上下文,因此如果不挖掘上下文的调用堆栈,就无法找到填充值的键,当然也无法找到父键(加载程序
知道
foo
bar
baz
,但不能用于确定哪个是对应的键或父键)

我建议您创建一个特殊的节点,使用
key\u construct
返回该节点,然后在YAML加载之后,遍历您的
YAML.load()结构
返回。除非您有其他
对象,这些对象使得遍历结果组合比单纯的序列/列表和映射/dicts组合更困难,否则:

import ruamel.yaml as yaml

yaml_str = """\
foo: &FOO
  bar: !.
  baz: !..

foo2:
  <<: *FOO
"""

class Expander:
    def __init__(self, tag):
        self.tag = tag

    def expand(self, key, parent_key):
        if self.tag == '!.':
            return key
        elif self.tag == '!..':
            return parent_key
        raise NotImplementedError

    def __repr__(self):
        return "E({})".format(self.tag)

def expand(d, key=None, parent_key=None):
    if isinstance(d, list):
        for elem in d:
            expand(elem, key=key, parent_key=parent_key)
    elif isinstance(d, dict):
        for k in d:
            v = d[k]
            if isinstance(v, Expander):
                d[k] = v.expand(k, parent_key)
            expand(d[k], key, parent_key=k)
    return d


def key_construct(loader, node):
    return Expander(node.tag)

yaml.add_constructor('!.', key_construct)
yaml.add_constructor('!..', key_construct)


data = yaml.load(yaml_str) 
print(data)
print(expand(data))


这是使用我是作者的方法完成的。PyYAML,其中ruamel.yaml是一个功能超集,应该也可以使用它。

这可能是一个剪切和粘贴错误,但是unicode字符2026,就像在源代码中一样,会给你一个扫描错误。你可能是指
..
而不是
..
。但一般来说,这些字符的开头和结尾都是f文档标记对于单个文档文件不是必需的。谢谢@Anthon,我实际上没有使用它们。我添加它们是为了清晰(区分代码和结果)谢谢@Anthon。如果我需要遍历结构,那么其他东西不是多余的吗?(添加构造函数并展开)我可以简单地删除感叹号并检查字符串值中的“.”或“.”。不过,我真希望有另一种方法。一种不需要再次遍历结构的方法,但我想我只能忍受这几毫秒的浪费:)@只有当您不希望任何字符串标量应该是
时,它才是多余的。
。如果你能接受这一点,你确实可以将整个过程简化为
expande()
function()。如果你想在不重新转换的情况下实现这一点,你需要更新解析器,以便在启动/结束新映射时,将当前键值的槽添加到某个堆栈中,这是比较容易的部分。如果您还希望使用定位点和别名,则需要使用定位点定义存储堆栈。我看到了这一点,当然这是可能的,但作为一个答案,“只是实施”的可能性很大
def expand_yaml(val, parent=None, key=None, key1=None, key2=None):
    if isinstance(val, str):
        if val == './':
            parent[key] = key1
        elif val == '../':
            parent[key] = key2
    elif isinstance(val, dict):
        for k, v in val.items():
            expand_yaml(v, val, k, k, key1)
    elif isinstance(val, list):
        parent[key] = val[:] # support inheritance of the list (as in *base_logger)
        for index, e in enumerate(parent[key]):
            expand_yaml(e, parent[key], index, key1, key2)
    return val
import ruamel.yaml as yaml

yaml_str = """\
foo: &FOO
  bar: !.
  baz: !..

foo2:
  <<: *FOO
"""

class Expander:
    def __init__(self, tag):
        self.tag = tag

    def expand(self, key, parent_key):
        if self.tag == '!.':
            return key
        elif self.tag == '!..':
            return parent_key
        raise NotImplementedError

    def __repr__(self):
        return "E({})".format(self.tag)

def expand(d, key=None, parent_key=None):
    if isinstance(d, list):
        for elem in d:
            expand(elem, key=key, parent_key=parent_key)
    elif isinstance(d, dict):
        for k in d:
            v = d[k]
            if isinstance(v, Expander):
                d[k] = v.expand(k, parent_key)
            expand(d[k], key, parent_key=k)
    return d


def key_construct(loader, node):
    return Expander(node.tag)

yaml.add_constructor('!.', key_construct)
yaml.add_constructor('!..', key_construct)


data = yaml.load(yaml_str) 
print(data)
print(expand(data))
{'foo': {'bar': E(!.), 'baz': E(!..)}, 'foo2': {'bar': E(!.), 'baz': E(!..)}}
{'foo': {'bar': 'bar', 'baz': 'foo'}, 'foo2': {'bar': 'bar', 'baz': 'foo2'}}