如何让argparse从带有选项而不是前缀的文件中读取参数

2024-03-29 14:48:23 发布

您现在位置:Python中文网/ 问答频道 /正文

我想知道如何使用python的argparse模块从命令行和文本文件中读取参数。我知道argparse的fromfile_prefix_chars但这并不是我想要的。我想要行为,但我不想要语法。我想要一个这样的界面:

$ python myprogram.py --foo 1 -A somefile.txt --bar 2

当argparse看到-A时,它应该停止从sys.argv或我给它的任何东西读取,并调用我编写的函数,该函数将读取somefile.text并返回参数列表。当文件耗尽时,它应该继续解析sys.argv或其他内容。文件中参数的处理必须按顺序进行(即应先处理-foo,然后再处理文件中的参数,然后再处理-bar,以便文件中的参数可以覆盖-foo,并且-bar可以覆盖文件中的内容)。

这样的事情有可能吗?我可以编写一个自定义函数,将新的参数推送到argparse的堆栈中,或者其他类似的东西吗?


Tags: 模块文件函数命令行内容参数prefixfoo
3条回答

一个Action在调用时,在其参数中获取parsernamespace

因此,您可以将文件通过前者更新后者:

class ArgfileAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        extra_args = <parse_the_file>(values)
        #`namespace' is updated in-place when specified
        parser.parse_args(extra_args,namespace)

你说的

I need to be able to write my own function to read that file and return the arguments (it's not in a one-argument-per-line format) –

现有前缀文件处理程序中有一个设置,用于更改读取文件的方式。文件由“private”方法parser._read_args_from_files读取,但它调用一个简单的公共方法,该方法将一行转换为字符串,默认情况下,每行操作一个参数:

def convert_arg_line_to_args(self, arg_line):
    return [arg_line]

它是这样写的,所以你可以很容易地定制它。 https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.convert_arg_line_to_args

A useful override of this method is one that treats each space-separated word as an argument:

def convert_arg_line_to_args(self, arg_line):
    for arg in arg_line.split():
        if not arg.strip():
            continue
        yield arg

test_argparse.pyunittesting文件中,有一个可供选择的测试用例。


但是如果您仍然希望用参数选项而不是前缀字符触发此读取,那么自定义操作方法是一个好方法。

您可以编写自己的函数,在传递给parser之前处理argv。它可以建立在parser._read_args_from_files上。

所以你可以编写这样的函数:

def read_my_file(argv):
    # if there is a '-A' string in argv, replace it, and the following filename
    # with the contents of the file (as strings)
    # you can adapt code from _read_args_from_files
    new_argv = []
    for a in argv:
        ....
        # details left to user
    return new_argv

然后使用以下命令调用解析器:

parser.parse_args(read_my_file(sys.argv[1:]))

是的,这可以包装在一个ArgumentParser子类中。

您可以使用自定义^{}来解决这个问题,该自定义^{}打开文件,解析文件内容,然后添加参数。

例如,这将是一个非常简单的操作:

class LoadFromFile (argparse.Action):
    def __call__ (self, parser, namespace, values, option_string = None):
        with values as f:
            parser.parse_args(f.read().split(), namespace)

你可以这样使用:

parser = argparse.ArgumentParser()
# other arguments
parser.add_argument('--file', type=open, action=LoadFromFile)
args = parser.parse_args()

然后,args中的结果命名空间也将包含从文件加载的任何配置。

如果需要更复杂的解析,还可以先单独解析文件内配置,然后有选择地选择应接管哪些值。例如,禁止在配置文件中指定另一个文件可能是有意义的:

def __call__ (self, parser, namespace, values, option_string=None):
    with values as f:
        contents = f.read()

    data = parser.parse_args(contents.split(), namespace=namespace)
    for k, v in vars(data).items():
        if v and k != option_string.lstrip('-'):
            setattr(namespace, k, v)

当然,您也可以使文件读取更复杂一些,例如首先从JSON读取。

相关问题 更多 >