【问题标题】:Overriding namespace with yaml使用 yaml 覆盖命名空间
【发布时间】:2018-10-21 06:27:35
【问题描述】:

问题陈述

我希望 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 对象中引入新选项时,维护一个相应更改的字典似乎是多余的。是否有任何解决方案可以为parser 对象中的每个参数获取type

【问题讨论】:

  • 您还可以使用action 参数定义自己的操作传递给add_argument,这些操作比您可以使用type 执行的操作更强大,您可能想研究一下。您可以以相同的方式查找并应用这些从 YAML 文件中获取的值。

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


【解决方案1】:

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

如果你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() 尽管你的配置文件 可能不够大而无法注意到。
  • 我使用的是文件扩展名.yaml,正如官方FAQ所推荐的那样。你也应该这样做,除非你不能(例如,如果你的 文件系统不允许这样做)。
  • 我在选项字符串--config-path 中使用破折号而不是下划线,这是 打字更自然一些,并自动转换为下划线以获得有效 标识符名称

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

【讨论】:

  • 感谢您的详细回复。顺便说一句,我会相应地更正我帖子中的术语。
  • 但是.parse_args() 不转换/应用类型参数,对吗?那么 args 将只是 yaml 将它们转换为的内容?当 argparse 通过 append ox extend 解析时,这可能会更成问题,对吧?有解决办法吗?
  • @ThomasHilger 如果您有任何问题,请照此发布,您的评论没有任何意义,(非)工作示例会有所帮助。您是否认为 yaml 将某些内容加载为整数并且没有通过显式调用解析器期望的选项类型进行转换,尽管它是使用 typ(v)) 进行转换的?
猜你喜欢
  • 1970-01-01
  • 2021-06-27
  • 2014-06-16
  • 2010-11-08
  • 2020-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多