如何在Yaml中将键的值用作另一个键的值?

时间:2019-09-16 08:36:44

标签: python yaml

我正在从yaml文件中读取参数,我想使用一个值来产生另一个值。例如,我需要以下参数a和b,b使用a的值。

A:
    a: 10
    b: a * 10
B:
    ......

这是我处理文件的方式:

from ruamel.yaml import safe_load, YAMLError

with open(yaml_file, 'r', encoding="utf-8") as hp:
    try:
        yaml_hparams = safe_load(hp)
        for _, V in yaml_hparams.items():
            for k, v in V.items():
                hparams[k] = v
    except YAMLError:
        print(YAMLError)

很显然,这为b的值提供了字符串“ a * 10”。我怎么给b加上* 10,即100?

2 个答案:

答案 0 :(得分:1)

不适用于YAML。 YAML是一种用于数据序列化的语言,它不是编程语言。

您有多种选择。一种是使用像Jinja这样的模板语言来预处理YAML并计算值。使用Jinja预处理YAML是在Ansible和SaltStack等工具中完成的,因此这是一种相当普遍的做法。但是,请注意Jinja不了解YAML的结构,因此您需要注意空格。

示例:

{% set val = 10 %}
A:
    a: {{ val }}
    b: {{ val * 10 }}

很明显,在加载YAML之前,您需要在输入上调用Jinja。


另一种选择是使用YAML标记来告诉您的代码如何对这些值进行后处理,并在其中实现它:

A:
    a: &val 10
    b: !prod [*val, 10]

代码类似于

from ruamel.yaml import safe_load, SafeLoader, YAMLError
import functools

def prod_constructor(loader, node):
    return functools.reduce(lambda x, y: x * y, loader.construct_sequence(node))

SafeLoader.add_constructor(u'!prod', prod_constructor)

with open(yaml_file, 'r', encoding="utf-8") as hp:
try:
    yaml_hparams = safe_load(hp)
    for _, V in yaml_hparams.items():
        for k, v in V.items():
            hparams[k] = v
except YAMLError:
    print(YAMLError)

答案 1 :(得分:0)

这是一个简单的概念证明,用于说明使用内置eval()函数执行此操作的可行性,正如我在评论中所建议的那样。因此,它具有一些局限性,例如无法处理“前向引用”,但我认为,如果需要,可以克服许多限制。

import json
from ruamel.yaml import safe_load, YAMLError


yaml_file = 'test_refs.yaml'

hparams = {}
with open(yaml_file, 'r', encoding="utf-8") as hp:
    try:
        yaml_hparams = safe_load(hp)
        for _, V in yaml_hparams.items():
            for k, v in V.items():
                hparams[k] = v
    except YAMLError as exc:
        print(exc)

print('Before:')
print(json.dumps(hparams, indent=4))   # Pretty-print result.

# Interpolate values with eval()
hparams['__builtins__'] = None  # Prevents access to built-ins.

for key, value in list(hparams.items()):
    if isinstance(value, str):
        try:
            hparams[key] = eval(value, hparams)
        except (NameError, TypeError):
            pass  # Possible forward-reference, ignore.

del hparams['__builtins__']  # No longer needed.

print()
print('After:')
print(json.dumps(hparams, indent=4))