Python Argparse:如何处理可变数量的参数(nargs='*')
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20165843/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Argparse: how to handle variable number of arguments (nargs='*')
提问by rubik
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.
我认为这nargs='*'足以处理可变数量的参数。显然不是,我不明白这个错误的原因。
The code:
编码:
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())
I think the resulting namespace should be Namespace(pos='1', foo='2', spam='8', vars=['8', '9']). Instead, argparse gives this error:
我认为生成的命名空间应该是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
Basically, argparse doesn't know where to put those additional arguments... Why is that?
基本上, argparse 不知道把这些额外的参数放在哪里......为什么会这样?
采纳答案by hpaulj
The relevant Python bug is Issue 15112.
相关的 Python 错误是Issue 15112。
argparse: nargs='*'positional argument doesn't accept any items if preceded by an option and another positional
argparse: nargs='*'如果前面有一个选项和另一个位置参数,则位置参数不接受任何项目
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 posand foo, and zero arguments for vars(remember *means ZERO_OR_MORE).
当 argparse 解析时,['1', '2', '--spam', '8', '8', '9']它首先尝试匹配['1','2']尽可能多的位置参数。使用您的参数,模式匹配字符串是AAA*:pos和各有 1 个参数foo,并且 0 个参数用于vars(记住*意味着 ZERO_OR_MORE)。
['--spam','8']are handled by your --spamargument. Since varshas already been set to [], there is nothing left to handle ['8','9'].
['--spam','8']由您的--spam论点处理。由于vars已设置为[],因此无需处理['8','9']。
The programming change to argparsechecks for the case where 0argument strings is satisfying the pattern, but there are still optionalsto be parsed. It then defers the handling of that *argument.
编程更改为argparse检查0参数字符串满足模式的情况,但仍有optionals待解析。然后它推迟处理该*参数。
You might be able to get around this by first parsing the input with parse_known_args, and then handling the remainderwith another call to parse_args.
您可以通过首先使用 解析输入parse_known_args,然后remainder使用另一个调用处理来解决此问题parse_args。
To have complete freedom in interspersing optionals among positionals, in issue 14191, I propose using parse_known_argswith just the optionals, followed by a parse_argsthat only knows about the positionals. The parse_intermixed_argsfunction that I posted there could be implemented in an ArgumentParsersubclass, without modifying the argparse.pycode itself.
为了完全自由地在位置之间穿插可选项,在issue 14191 中,我建议parse_known_args只使用optionals,后跟parse_args只知道位置的 a 。parse_intermixed_args我在那里发布的函数可以在ArgumentParser子类中实现,而无需修改argparse.py代码本身。
Here's a way of handling subparsers. I've taken the parse_known_intermixed_argsfunction, simplified it for presentation sake, and then made it the parse_known_argsfunction of a Parser subclass. I had to take an extra step to avoid recursion.
这是一种处理子解析器的方法。我已经采用了这个parse_known_intermixed_args函数,为了演示的缘故简化了它,然后使它parse_known_args成为 Parser 子类的函数。我不得不采取额外的步骤来避免递归。
Finally I changed the _parser_classof the subparsers Action, so each subparser uses this alternative parse_known_args. An alternative would be to subclass _SubParsersAction, possibly modifying its __call__.
最后我改变了_parser_class子解析器的 Action,所以每个子解析器都使用这个替代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')
回答by caleb531
Simple solution: Specify the --spamflag before specifying posand foo:
简单的解决方案:--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())
The same works if you place the --spamflag after specifying your variable arguments.
如果--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())
EDIT: For what it's worth, it seems that changing the *to a +will also fix the error.
编辑:对于它的价值,似乎*将 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())
回答by HoangYell
For anyone who doesn't know what is nargs:
对于不知道是什么的人nargs:
nargsstands for Number Of Arguments
nargs代表 Number Of Arguments
3: 3 values, can be any number you want?: a single value, which can be optional*: a flexible number of values, which will be gathered into a list+: like *, but requiring at least one valueargparse.REMAINDER: all the values that are remaining in the command line
3: 3 个值,可以是您想要的任何数字?: 单个值,可以是可选的*: 一个灵活数量的值,这些值将被收集到一个列表中+: 类似 *,但至少需要一个值argparse.REMAINDER: 命令行中剩余的所有值
Example:
例子:
Python
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)
Console
安慰
$ 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]

