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

时间:2013-11-23 17:57:36

标签: 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())

我认为生成的命名空间应为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

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

3 个答案:

答案 0 :(得分:30)

相关的Python错误是Issue 15112

  如果前面有一个选项和另一个位置

argparse: nargs='*'位置参数不接受任何项目

当argparse解析['1', '2', '--spam', '8', '8', '9']时,它首先尝试将['1','2']与尽可能多的位置参数匹配。使用您的参数,模式匹配字符串为AAA*posfoo分别为1个参数,vars为零参数(记住*表示ZERO_OR_MORE)。

['--spam','8']由您的--spam参数处理。由于vars已设置为[],因此无法处理['8','9']

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

您可以通过首先使用parse_known_args解析输入,然后再次调用parse_args处理remainder来解决此问题。

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


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

最后,我更改了子分段Action的_parser_class,因此每个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')

答案 1 :(得分:14)

对于任何不知道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)

控制台

$ 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]

See more

答案 2 :(得分:9)

简单解决方案:在指定--spampos之前指定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())

如果在指定变量参数后放置--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())

编辑:对于它的价值,似乎将*更改为+也可以解决错误。

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())