Argparse:如何处理可变数量的参数(nargs ='*')

rub*_*bik 52 python argparse

我认为这nargs='*'足以处理可变数量的参数.显然它不是,我不明白这个错误的原因.

代码:

p = argparse.ArgumentParser()
p.add_argument('pos')
p.add_argument('foo')
p.add_argument('--spam', default=24, type=int, dest='spam')
p.add_argument('vars', nargs='*')

p.parse_args('1 2 --spam 8 8 9'.split())
Run Code Online (Sandbox Code Playgroud)

我认为最终的命名空间应该是Namespace(pos='1', foo='2', spam='8', vars=['8', '9']).相反,argparse给出了这个错误:

usage: prog.py [-h] [--spam SPAM] pos foo [vars [vars ...]]
error: unrecognized arguments: 9 8
Run Code Online (Sandbox Code Playgroud)

基本上,argparse不知道在哪里放这些额外的参数......为什么?

Hoa*_*ell 36

对于不知道是什么的人nargs

nargs 代表 Number Of Arguments

  • 3: 3 个值,可以是您想要的任何数字
  • ?: 单个值,可以是可选的
  • *: 一个灵活数量的值,这些值将被收集到一个列表中
  • +: 类似 *,但至少需要一个值
  • argparse.REMAINDER: 命令行中剩余的所有值

例子:

Python

import argparse

my_parser = argparse.ArgumentParser()
my_parser.add_argument('--input', action='store', type=int, nargs=3)

args = my_parser.parse_args()

print(args.input)
Run Code Online (Sandbox Code Playgroud)

安慰

$ python nargs_example.py --input 42
usage: nargs_example.py [-h] [--input INPUT INPUT INPUT]
nargs_example.py: error: argument --input: expected 3 arguments

$ python nargs_example.py --input 42 42 42
[42, 42, 42]
Run Code Online (Sandbox Code Playgroud)

查看更多


hpa*_*ulj 34

问题相关的Python是问题15112.

argparse: nargs='*' 如果前面有一个选项和另一个位置,则位置参数不接受任何项目

当argparse解析['1', '2', '--spam', '8', '8', '9']它时,它首先尝试匹配['1','2']尽可能多的位置参数.使用您的参数,模式匹配字符串是AAA*:1个参数pos和for foo,以及零参数vars(记住*意味着ZERO_OR_MORE).

['--spam','8']由你的--spam论点处理.既然vars已经设定[],没有什么可以处理的['8','9'].

编程更改以argparse检查0参数字符串是否满足模式的情况,但仍有optionals待解析.然后它推迟对该*论点的处理.

您可以通过首先解析输入parse_known_args,然后处理remainder另一个调用来解决这个问题parse_args.

为了完全自由地在位置之间穿插选项,在问题14191中,我建议parse_known_args只使用optionals,然后parse_args只知道位置.parse_intermixed_args我在那里发布的函数可以在ArgumentParser子类中实现,而无需修改argparse.py代码本身.


这是一种处理子分析器的方法.我已经采用了这个parse_known_intermixed_args函数,为了演示而简化它,然后使它parse_known_args成为Parser子类的功能.我不得不采取额外措施来避免递归.

最后我更改了_parser_class子分析器Action,因此每个subparser都使用了这个替代方案parse_known_args.另一种方法是子类化_SubParsersAction,可能修改它__call__.

from argparse import ArgumentParser

def parse_known_intermixed_args(self, args=None, namespace=None):
    # self - argparse parser
    # simplified from http://bugs.python.org/file30204/test_intermixed.py
    parsefn = super(SubParser, self).parse_known_args # avoid recursion

    positionals = self._get_positional_actions()
    for action in positionals:
        # deactivate positionals
        action.save_nargs = action.nargs
        action.nargs = 0

    namespace, remaining_args = parsefn(args, namespace)
    for action in positionals:
        # remove the empty positional values from namespace
        if hasattr(namespace, action.dest):
            delattr(namespace, action.dest)
    for action in positionals:
        action.nargs = action.save_nargs
    # parse positionals
    namespace, extras = parsefn(remaining_args, namespace)
    return namespace, extras

class SubParser(ArgumentParser):
    parse_known_args = parse_known_intermixed_args

parser = ArgumentParser()
parser.add_argument('foo')
sp = parser.add_subparsers(dest='cmd')
sp._parser_class = SubParser # use different parser class for subparsers
spp1 = sp.add_parser('cmd1')
spp1.add_argument('-x')
spp1.add_argument('bar')
spp1.add_argument('vars',nargs='*')

print parser.parse_args('foo cmd1 bar -x one 8 9'.split())
# Namespace(bar='bar', cmd='cmd1', foo='foo', vars=['8', '9'], x='one')
Run Code Online (Sandbox Code Playgroud)

  • 现在问题 14191 已修复,可以进行更新 (2认同)

cal*_*531 12

简单解决方案:--spam在指定之前指定标志,pos并且foo:

p = argparse.ArgumentParser()
p.add_argument('pos')
p.add_argument('foo')
p.add_argument('--spam', default=24, type=int, dest='spam')
p.add_argument('vars', nargs='*')

p.parse_args('--spam 8 1 2 8 9'.split())
Run Code Online (Sandbox Code Playgroud)

如果--spam在指定变量参数后放置标志,则同样有效.

p = argparse.ArgumentParser()
p.add_argument('pos')
p.add_argument('foo')
p.add_argument('--spam', default=24, type=int, dest='spam')
p.add_argument('vars', nargs='*')

p.parse_args('1 2 8 9 --spam 8'.split())
Run Code Online (Sandbox Code Playgroud)

编辑:对于它的价值,似乎更改*为a +也将修复错误.

p = argparse.ArgumentParser()
p.add_argument('pos')
p.add_argument('foo')
p.add_argument('--spam', default=24, type=int, dest='spam')
p.add_argument('vars', nargs='+')

p.parse_args('1 2 --spam 8 8 9'.split())
Run Code Online (Sandbox Code Playgroud)

  • 是的,我可以做到,但那不是我想要的.我正在构建一个命令行工具,所以我不是那个会编写参数的人.我不能强迫用户使用特定的解决方案.我写的代码应该有用,应该是灵活的.我真的不知道为什么会失败. (4认同)
  • 是的,但很可能 `argparse` 无法处理这种特殊情况。参数解析的规则也很可能不允许以这种方式定位参数。无论如何,我已经用另一个潜在的解决方案 FWIW 修改了我的答案。 (2认同)