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

Python Argparse:如何处理可变数量的参数(nargs=';*';),python,argparse,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'.spli

我认为
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())
我认为产生的名称空间应该是
名称空间(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不知道把这些附加参数放在哪里。。。为什么会这样?

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

相关的Python错误是

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

当argparse解析
['1','2','--spam','8','8','9']
时,它首先尝试将
['1','2']
与尽可能多的位置参数匹配。对于参数,模式匹配字符串是
AAA*
:pos和
foo
各有一个参数,vars的参数为零(请记住
*
表示零或更多)

['--spam','8']
--spam
参数处理。由于
vars
已被设置为
[]
,因此没有什么可处理的
['8','9']

编程更改为
argparse
检查
0
参数字符串是否满足模式,但仍有
选项要解析。然后它会延迟处理该
*
参数

您可以通过首先使用解析输入,然后使用另一个调用来处理
余数来解决这个问题

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


这里有一种处理次级抵押的方法。我采用了这个函数,为了表示的缘故对其进行了简化,然后将其作为解析器子类的
parse\u known\u args
函数。我不得不采取额外的步骤来避免递归

最后,我更改了subparser操作的
\u parser\u类
,因此每个subparser都使用这个可选的
parse\u known\u args
。另一种方法是子类化
\u subparseraction
,可能修改其
\u调用

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

对于那些不知道什么是“纳格斯”的人:

nargs
表示
参数数

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

Python

控制台


是的,我可以这样做,但那不是我想要的。我正在构建一个命令行工具,所以我不是编写参数的人。我无法强制用户使用特定的解决方案。我写的代码应该可以工作并且应该是灵活的。我真的不知道它为什么会失败。没错,但很可能
argparse
无法处理这种特殊情况。参数解析规则也很可能不允许以这种方式定位参数。无论如何,我已经用另一个可能的解决方案修改了我的答案,FWIW。你是对的,正如另一个答案所描述的,最终它变成了一个argparse错误。哦,这是个好消息。那么你是说,如果我接受你添加的两个函数,并在我自己的子类中实现,我就能解决这个问题?关于
\u get\u values()
函数,是否必须对其进行两行更改?我发现很难理解如何使用
SUPPRESS
。哦,不,坏消息刚到。我读到这与Subparser不兼容,这是我绝对需要的特性。这是暂时的还是根本不可能实现?本能地,我会说这应该是可能的,因为subparser被指定为第二个参数,然后解析正常进行。我错了吗?我尝试了两种不同的方法暂时禁用
位置
参数。第一个是
nargs=0
。第二个是
nargs=SUPPRESS
。如果实现自己的子类,我将使用第一个
SUPPRESS
有一些优点,但需要对“argparse”进行更深入的更改。是修补您自己的解析器的一个更好的示例。现在问题14191已修复,可以进行更新
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]