使用 python 访问 yaml 中的元素

Access elements inside yaml using python

我正在使用 yaml 和 pyyaml 配置我的应用程序。

是否可以这样配置-

config.yml -

root:
    repo_root: /home/raghhuveer/code/data_science/papers/cv/AlexNet_lght
    data_root: $root.repo_root/data

service:
    root: $root.data_root/csv/xyz.csv

yaml加载函数-

def load_config(config_path):
    config_path = os.path.abspath(config_path)
    
    if not os.path.isfile(config_path):
        raise FileNotFoundError("{} does not exist".format(config_path))
    else:
        with open(config_path) as f:
            config = yaml.load(f, Loader=yaml.SafeLoader)
        # logging.info(config)
        logging.info("Config used for run - \n{}".format(yaml.dump(config, sort_keys=False)))
        return DotDict(config)

当前输出-

root:
  repo_root: /home/raghhuveer/code/data_science/papers/cv/AlexNet_lght
  data_root: ${root.repo_root}/data

service:
  root: ${root.data_root}/csv/xyz.csv

期望输出 -

root:
  repo_root: /home/raghhuveer/code/data_science/papers/cv/AlexNet_lght
  data_root: /home/raghhuveer/code/data_science/papers/cv/AlexNet_lght/data

service:
  root: /home/raghhuveer/code/data_science/papers/cv/AlexNet_lght/data/csv/xyz.csv

python甚至可以做到这一点吗?如果是这样,任何帮助都会非常好。

提前致谢。

一般方法:

  • 按原样读取文件
  • 搜索包含 $ 的字符串:
    • 确定“变量”的“路径”
    • 用实际值替换“变量”

一个例子,使用递归调用字典和替换字符串:

import re, pprint, yaml

def convert(input,top=None):
    """Replaces $key1.key2 with actual values. Modifies input in-place"""
    if top is None:
        top = input # top should be the original input
    if isinstance(input,dict):
        ret = {k:convert(v,top) for k,v in input.items()} # recursively convert items
        if input != ret: # in case order matters, do it one or several times more until no change happens
            ret = convert(ret)
        input.update(ret) # update original input
        return input # return updated input (for the case of recursion)
    if isinstance(input,str):
        vars = re.findall(r"$[\w_\.]+",input) # find $key_1.key_2.keyN sequences
        for var in vars:
            keys = var[1:].split(".") # remove dollar and split by dots to make "key chain"
            val = top # starting from top ...
            for k in keys: # ... for each key in the key chain ...
                val = val[k] # ... go one level down
            input = input.replace(var,val) # replace $key sequence eith actual value
        return input # return modified input
    # TODO int, float, list, ...

with open("in.yml") as f: config = yaml.load(f) # load as is
convert(config) # convert it (in-place)
pprint.pprint(config)

输出:

{'root': {'data_root': '/home/raghhuveer/code/data_science/papers/cv/AlexNet_lght/data',
          'repo_root': '/home/raghhuveer/code/data_science/papers/cv/AlexNet_lght'},
 'service': {'root': '/home/raghhuveer/code/data_science/papers/cv/AlexNet_lght/data/csv/xyz.csv'}}

注意:YAML 在这里并不那么重要,它也适用于 JSON、XML 或其他格式。

注意 2:如果您只使用 YAML 并且只使用 python,this post 的一些答案可能会有用(使用锚点和引用以及特定于应用程序的本地标签)