如何使用 argparse 将输入文件中的值附加到命令行选项?

How can I append the values from an input file to the command line options using argparse?

假设我有一个包含以下内容的 yaml 输入文件 (input.txt):

names: [Bob, Jill]
ages: [22, 31]
county: somewhere

我的解析代码:

import yaml
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--name', default=[], dest='names', action='append')
parser.add_argument('--age', default=[], dest='ages', action='append')
parser.add_argument('--county', dest='county')
parser.add_argument(
    '--config-file',
    dest='config_file',
    type=argparse.FileType(mode='r'))
args = parser.parse_args()
print args

我的问题是:我看到很多网站展示了如何像上面那样为配置文件添加参数。我如何 'parse' 该配置文件并将其中的值添加(附加)到命令行上传递的任何值?

如果我的程序叫做so.py,并且是这样调用的:

so.py --config-file=input.txt --name 'Ralph' --age 40

我想得到 名字 = ['Bob', 'Jill', 'Ralph'] 年龄 = [22、31、40] 县 = 'somewhere' 在我的参数中。这可能吗?

有一个参数允许从文件输入,'fromfile-prefix-chars'

https://docs.python.org/3/library/argparse.html#fromfile-prefix-chars

在默认形式下,每行需要一个命令行字符串。但是文档还展示了如何修改它以从每一行中获取多个字符串。您可以采用该想法并将其扩展以处理 Yaml 语法。

但是有些库可以读取配置文件和 yaml 文件。因此,您可以使用 --config-file 参数通过该库读取文件,然后将其结果与 argparse 结果合并。通过将(yaml 和 argparse)都转换为字典并使用 .update 方法,合并可能是最简单的。

Ipython 使用它的配置文件(默认的和用户指定的)来填充它的 argparse 解析器参数。因此,用户配置文件可以覆盖默认值,命令行可以覆盖两者。执行此操作的代码不适合胆小的人,但对于大型项目可能值得研究。

如何进行合并的详细信息取决于 'yaml' 生成的对象。 argparse args 是类型 argparse.Namespace 的简单对象。有关详细信息,请参阅文档。通常人们只是 args 中的 'read' 个值,但添加或修改值同样容易。如果您愿意,vars(args) 可以将其变成字典。


使用 yaml.load(并稍微简化 argparse):

import yaml
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--names', action='append')
parser.add_argument('--ages', action='append')
parser.add_argument('--county', dest='county')
parser.add_argument('--config-file')   # automatic - to _ conversion

sample = "--config-file=stack28220581.yaml --name Ralph --age 40".split()
args = parser.parse_args(sample)
# add test that len(args.names)==len(args.ages) ??
with open(args.config_file) as f:
    ydict = yaml.load(f)
# {'county': 'somewhere', 'names': ['Bob', 'Jill'], 'ages': [22, 31]}

# add list attributes from args to the corresponding ydict values
for k,v in ydict.items():
    av = getattr(args,k,None)
    if av and isinstance(v, list):
        v.extend(av)
print(ydict)
# {'names': ['Bob', 'Jill', 'Ralph'], 'ages': [22, 31, '40'], 'county': 'somewhere'}

使用prefix_chars的例子:

def foo(astr):
    # custom convert_arg_line_to_args method
    # convert 'names: [v1,v2]' into ['--names', v1, '--names', v2, ...]
    alist = []
    if ':' not in astr:
        return astr
    field,value = astr.split(':')
    value = value.strip()
    field = '--'+field
    if value.startswith('['):
        values = value[1:-1].split(',')
        for v in values:
            alist.extend([field,v.strip()])
    else:
        alist.extend([field, value])
    return alist

parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
parser.convert_arg_line_to_args = foo  # could also do this with subclass
parser.add_argument('--names', action='append')
parser.add_argument('--ages', action='append')
parser.add_argument('--county', dest='county')
sample = "@stack28220581.yaml --name Ralph --age 40".split()
args = parser.parse_args(sample)
print(args)
# Namespace(ages=['22', '31', '40'], county='somewhere', names=['Bob', 'Jill', 'Ralph'])