如何创建具有多个位置参数的argparse互斥组?

sea*_*ern 11 python command-line-arguments argparse

我正在尝试解析命令行参数,以便下面的三种可能性是可能的:

script
script file1 file2 file3 …
script -p pattern
Run Code Online (Sandbox Code Playgroud)

因此,文件列表是可选的.如果-p pattern指定了一个选项,则命令行上不能有任何其他选项.以"用法"格式表示,它可能看起来像这样:

script [-p pattern | file [file …]]
Run Code Online (Sandbox Code Playgroud)

我认为用Python的argparse模块做到这一点的方法是这样的:

parser = argparse.ArgumentParser(prog=base)
group = parser.add_mutually_exclusive_group()
group.add_argument('-p', '--pattern', help="Operate on files that match the glob pattern")
group.add_argument('files', nargs="*", help="files to operate on")
args = parser.parse_args()
Run Code Online (Sandbox Code Playgroud)

但Python抱怨我的位置参数需要是可选的:

Traceback (most recent call last):
  File "script", line 92, in <module>
    group.add_argument('files', nargs="*", help="files to operate on")
…
ValueError: mutually exclusive arguments must be optional
Run Code Online (Sandbox Code Playgroud)

argparse文档说该"*"参数nargs意味着它是可选的.

我也无法找到任何其他价值nargs.我最接近的是使用nargs="?",但只抓取一个文件,而不是任何数字的可选列表.

是否可以使用argparse?组合这种参数语法?

hpa*_*ulj 7

简短的回答

添加default*位置

引发错误的代码是,

    if action.required:
        msg = _('mutually exclusive arguments must be optional')
        raise ValueError(msg)
Run Code Online (Sandbox Code Playgroud)

如果我向解析器添加*,我看到该required属性已设置:

In [396]: a=p.add_argument('bar',nargs='*')
In [397]: a
Out[397]: _StoreAction(option_strings=[], dest='bar', nargs='*', const=None, default=None, type=None, choices=None, help=None, metavar=None)
In [398]: a.required
Out[398]: True
Run Code Online (Sandbox Code Playgroud)

而对于一个?它将是假的.我将在代码中进一步挖掘,看看为什么会有区别.它可能是一个错误或被忽视的"功能",或者可能是一个很好的理由."可选"定位的一个棘手的问题是,无应答是一个答案,也就是说,一个空的值列表是有效的.

In [399]: args=p.parse_args([])
In [400]: args
Out[400]: Namespace(bar=[], ....)
Run Code Online (Sandbox Code Playgroud)

因此,互为排斥必须有一些方法来区分默认[]和真实[].

现在--files,如果您希望argparse执行互斥测试,我建议使用标记参数而不是位置参数.


设置required位置属性的代码是:

    # mark positional arguments as required if at least one is
    # always required
    if kwargs.get('nargs') not in [OPTIONAL, ZERO_OR_MORE]:
        kwargs['required'] = True
    if kwargs.get('nargs') == ZERO_OR_MORE and 'default' not in kwargs:
        kwargs['required'] = True
Run Code Online (Sandbox Code Playgroud)

因此解决方案是指定默认值 *

In [401]: p=argparse.ArgumentParser()
In [402]: g=p.add_mutually_exclusive_group()
In [403]: g.add_argument('--foo')
Out[403]: _StoreAction(option_strings=['--foo'], dest='foo', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)
In [404]: g.add_argument('files',nargs='*',default=None)
Out[404]: _StoreAction(option_strings=[], dest='files', nargs='*', const=None, default=None, type=None, choices=None, help=None, metavar=None)
In [405]: p.parse_args([])
Out[405]: Namespace(files=[], foo=None)
Run Code Online (Sandbox Code Playgroud)

默认甚至可以[].解析器能够区分您提供的默认值和未使用的默认值.

哎呀 - default=None错了.它传递add_argumentrequired测试,但产生相互排斥的错误.详细信息在于代码如何区分用户定义的默认值和自动默认值.因此,使用任何东西,但None.

我在文档中没有看到任何关于此的内容.我将不得不检查错误/问题,看看它已讨论过的主题.它之前也可能出现在SO上.

  • 这完全做到了。将 `default=[]` 添加到带有 `nargs="*"` 的参数在所有情况下都会得到正确的结果。 (3认同)