使用yaml覆盖名称空间

时间:2018-10-21 06:27:35

标签: python-3.x yaml parameter-passing

问题陈述

我希望python模块支持的选项可以被.yaml文件覆盖,因为在某些情况下,有太多的选项需要用非默认值指定。

我实现了如下逻辑。

parser = argparse.ArgumentParser()
# some parser.add statements that comes with default values
parser.add_argument("--config_path", default=None, type=str,
                    help="A yaml file for overriding parameters specification in this module.")
args = parser.parse_args()

# Override parameters
if args.config_path is not None:
    with open(args.config_path, "r") as f:
        yml_config = yaml.safe_load(f)
    for k, v in yml_config.items():
        if k in args.__dict__:
            args.__dict__[k] = v
        else:
            sys.stderr.write("Ignored unknown parameter {} in yaml.\n".format(k))

问题是,对于某些选项,我有特定的函数/ lambda表达式来转换输入字符串,例如:

parser.add_argument("--tokens", type=lambda x: x.split(","))

为了在YAML中解析选项规范时应用相应的功能,添加如此多的if语句似乎不是一个好的解决方案。维护在parser对象中引入新选项时相应更改的字典似乎是多余的。有什么解决方法可以为type对象中的每个参数获取parser

1 个答案:

答案 0 :(得分:1)

如果您使用add_argument添加到解析器的元素以--开头,则它们 实际上是可选的,通常称为选项。你会发现这些走过 _get_optonal_actions()实例的parser方法的结果。

如果您config.yaml看起来像:

tokens: a,b,c

,那么您可以这样做:

import sys
import argparse
import ruamel.yaml


sys.argv[1:] = ['--config-path', 'config.yaml']  # simulate commandline

yaml = ruamel.yaml.YAML(typ='safe')

parser = argparse.ArgumentParser()
parser.add_argument("--config-path", default=None, type=str,
                    help="A yaml file for overriding parameters specification in this module.")
parser.add_argument("--tokens", type=lambda x: x.split(","))
args = parser.parse_args()


def find_option_type(key, parser):
    for opt in parser._get_optional_actions():
        if ('--' + key) in opt.option_strings:
           return opt.type
    raise ValueError

if args.config_path is not None:
    with open(args.config_path, "r") as f:
        yml_config = yaml.load(f)
    for k, v in yml_config.items():
        if k in args.__dict__:
            typ = find_option_type(k, parser)
            args.__dict__[k] = typ(v)
        else:
            sys.stderr.write("Ignored unknown parameter {} in yaml.\n".format(k))

print(args)

给出:

Namespace(config_path='config.yaml', tokens=['a', 'b', 'c'])

请注意:

  • 我正在使用ruamel.yaml的新API。这样加载实际上更快 比使用from ruamel import yaml; yaml.safe_load()要好,尽管您的配置文件 可能还不足以引起注意。
  • 我正在使用FAQ官方建议的文件扩展名.yaml。您也应该这样做,除非您不能这样做(例如,如果您的 文件系统不允许这样做。
  • 我在选项字符串--config-path中使用破折号而不是下划线,这是 键入自然一些,并自动转换为下划线以获取有效 标识符名称

您可能需要考虑采用另一种方法手动解析sys.argv --config-path,然后为YAML配置文件中的所有选项设置默认值,然后 然后致电.parse_args()。按此顺序进行操作可以覆盖 命令行,在配置文件中有一个值。如果您以自己的方式做事,您总是 如果配置文件具有除一个以外的所有正确值,则必须编辑该配置文件。