Argparse:如何处理可变数量的参数(nargs='*') [英] Argparse: how to handle variable number of arguments (nargs='*')
问题描述
我认为 nargs='*'
足以处理可变数量的参数.显然不是,我不明白这个错误的原因.
I thought that nargs='*'
was enough to handle a variable number of arguments. Apparently it's not, and I don't understand the cause of this error.
代码:
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 给出了这个错误:
I think the resulting namespace should be Namespace(pos='1', foo='2', spam='8', vars=['8', '9'])
. Instead, argparse gives this error:
usage: prog.py [-h] [--spam SPAM] pos foo [vars [vars ...]]
error: unrecognized arguments: 9 8
基本上,argparse 不知道把这些额外的参数放在哪里......为什么会这样?
Basically, argparse doesn't know where to put those additional arguments... Why is that?
推荐答案
相关的 Python 错误是 Issue 15112.
The relevant Python bug is Issue 15112.
argparse: nargs='*'
如果前面有一个选项和另一个位置参数,则位置参数不接受任何项
argparse: nargs='*'
positional argument doesn't accept any items if preceded by an option and another positional
当 argparse 解析 ['1', '2', '--spam', '8', '8', '9']
它首先尝试匹配 ['1','2']
使用尽可能多的位置参数.使用您的参数,模式匹配字符串为 AAA*
:pos
和 foo
各有 1 个参数,vars的参数为零code>(记住
*
表示零或更多).
When argparse parses ['1', '2', '--spam', '8', '8', '9']
it first tries to match ['1','2']
with as many of the positional arguments as possible. With your arguments the pattern matching string is AAA*
: 1 argument each for pos
and foo
, and zero arguments for vars
(remember *
means ZERO_OR_MORE).
['--spam','8']
由您的 --spam
参数处理.由于 vars
已经设置为 []
,所以没有什么可以处理 ['8','9']
.
['--spam','8']
are handled by your --spam
argument. Since vars
has already been set to []
, there is nothing left to handle ['8','9']
.
对argparse
的编程更改检查0
参数字符串是否满足模式的情况,但仍有optionals
需要解析.然后它推迟处理 *
参数.
The programming change to argparse
checks for the case where 0
argument strings is satisfying the pattern, but there are still optionals
to be parsed. It then defers the handling of that *
argument.
您可能可以通过首先使用 parse_known_args
,然后通过另一个对 parse_args
.
You might be able to get around this by first parsing the input with parse_known_args
, and then handling the remainder
with another call to parse_args
.
为了在位置之间穿插可选项有完全的自由,在 issue 14191 中,我建议使用 parse_known_args
只包含 optionals
,后跟一个只知道位置的 parse_args
.我在那里发布的 parse_intermixed_args
函数可以在 ArgumentParser
子类中实现,而无需修改 argparse.py
代码本身.
To have complete freedom in interspersing optionals among positionals, in issue 14191, I propose using parse_known_args
with just the optionals
, followed by a parse_args
that only knows about the positionals. The parse_intermixed_args
function that I posted there could be implemented in an ArgumentParser
subclass, without modifying the argparse.py
code itself.
这是一种处理子解析器的方法.我采用了 parse_known_intermixed_args
函数,简化了它的演示为了,然后使它成为解析器子类的 parse_known_args
函数.我不得不采取额外的步骤来避免递归.
Here's a way of handling subparsers. I've taken the parse_known_intermixed_args
function, simplified it for presentation sake, and then made it the parse_known_args
function of a Parser subclass. I had to take an extra step to avoid recursion.
最后我改变了子解析器动作的_parser_class
,所以每个子解析器都使用这个替代的parse_known_args
.另一种方法是将 _SubParsersAction
子类化,可能会修改其 __call__
.
Finally I changed the _parser_class
of the subparsers Action, so each subparser uses this alternative parse_known_args
. An alternative would be to subclass _SubParsersAction
, possibly modifying its __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')
这篇关于Argparse:如何处理可变数量的参数(nargs='*')的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!