Python argparse 位置参数和子命令
我正在使用 argparse 并尝试混合子命令和位置参数,出现了以下问题。
此代码运行良好:
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
parser.add_argument('positional')
subparsers.add_parser('subpositional')
parser.parse_args('subpositional positional'.split())
上面的代码将参数解析为 Namespace(positional='positional')
,但是当我将位置参数更改为 nargs='?' 时因此:
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
parser.add_argument('positional', nargs='?')
subparsers.add_parser('subpositional')
parser.parse_args('subpositional positional'.split())
它会出错:
usage: [-h] {subpositional} ... [positional]
: error: unrecognized arguments: positional
这是为什么?
I'm working with argparse and am trying to mix sub-commands and positional arguments, and the following issue came up.
This code runs fine:
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
parser.add_argument('positional')
subparsers.add_parser('subpositional')
parser.parse_args('subpositional positional'.split())
The above code parses the args into Namespace(positional='positional')
, however when I change the positional argument to have nargs='?' as such:
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
parser.add_argument('positional', nargs='?')
subparsers.add_parser('subpositional')
parser.parse_args('subpositional positional'.split())
It errors out with:
usage: [-h] {subpositional} ... [positional]
: error: unrecognized arguments: positional
Why is this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
起初我认为与 jcollado 相同,但事实是,如果后续(顶级)位置参数具有特定的
nargs
(nargs
=None
,nargs
= 整数),那么它就会按您的预期工作。当nargs
为'?'
或'*'
时失败,有时当它为'+'
时失败。因此,我深入研究代码,弄清楚发生了什么。它归结为参数被分割以供使用的方式。为了弄清楚谁得到了什么,对
parse_args
的调用将参数总结为'AA'
等字符串,在您的情况下 ('A'
对于位置参数,'O'
表示可选),最终生成一个与该摘要字符串匹配的正则表达式模式,具体取决于您通过添加到解析器的操作。 add_argument
和.add_subparsers
方法。在每种情况下,例如,参数字符串最终都是
'AA'
。改变的是要匹配的模式(您可以在argparse.py
中的_get_nargs_pattern
下看到可能的模式。对于subpositional
,它最终是 < code>'(-*A[-AO]*)',这意味着允许一个参数后跟任意数量的选项或参数对于位置
,这取决于传递给的值nargs
:无
=>'(-*A-*)'
'(-*A-* A-*A-*)'
(每个预期参数一个'-*A'
)'?'
=>'(-* A?-*)'
'*'
=>'(-*[A-]*)'
'+'
=>'(-*A[A-]*)'< /code>
这些模式被附加,对于
nargs=None
(您的工作示例),您最终会得到'(-*A[-AO]*)(-*A-* )'
,匹配两个组['A', 'A']
这样,subpositional
将仅解析subpositional
(您想要的),而positional
将匹配其操作。但是,对于
nargs='?'
,您最终会得到'(-*A[-AO]*)(-*A?-*)'
。第二组完全由可选模式组成,并且*
是贪婪的,这意味着第一组通配字符串中的所有内容,最终识别出两个组[ 'AA','']
。这意味着subpositional
有两个参数,当然最终会令人窒息。有趣的是,
nargs='+'
的模式是'(-*A[-AO]*)(-*A[A-]*)'
,其中只要你只传递一个参数就可以工作。说subpositional a
,因为您需要在第二组中至少有一个位置参数。同样,由于第一组是贪婪的,因此传递subpositional abc d
会得到['AAAA', 'A']
,这不是您想要的。简而言之:一团糟。我想这应该被认为是一个错误,但不确定如果模式变成非贪婪模式会产生什么影响......
At first I thought the same as jcollado, but then there's the fact that, if the subsequent (top level) positional arguments have a specific
nargs
(nargs
=None
,nargs
= integer), then it works as you expect. It fails whennargs
is'?'
or'*'
, and sometimes when it is'+'
. So, I went down to the code, to figure out what is going on.It boils down to the way the arguments are split to be consumed. To figure out who gets what, the call to
parse_args
summarizes the arguments in a string like'AA'
, in your case ('A'
for positional arguments,'O'
for optional), and ends up producing a regex pattern to be matched with that summary string, depending on the actions you've added to the parser through the.add_argument
and.add_subparsers
methods.In every case, for you example, the argument string ends up being
'AA'
. What changes is the pattern to be matched (you can see the possible patterns under_get_nargs_pattern
inargparse.py
. Forsubpositional
it ends up being'(-*A[-AO]*)'
, which means allow one argument followed by any number of options or arguments. Forpositional
, it depends on the value passed tonargs
:None
=>'(-*A-*)'
'(-*A-*A-*A-*)'
(one'-*A'
per expected argument)'?'
=>'(-*A?-*)'
'*'
=>'(-*[A-]*)'
'+'
=>'(-*A[A-]*)'
Those patterns are appended and, for
nargs=None
(your working example), you end up with'(-*A[-AO]*)(-*A-*)'
, which matches two groups['A', 'A']
. This way,subpositional
will parse onlysubpositional
(what you wanted), whilepositional
will match its action.For
nargs='?'
, though, you end up with'(-*A[-AO]*)(-*A?-*)'
. The second group is comprised entirely of optional patterns, and*
being greedy, that means the first group globs everything in the string, ending up recognizing the two groups['AA', '']
. This meanssubpositional
gets two arguments, and ends up choking, of course.Funny enough, the pattern for
nargs='+'
is'(-*A[-AO]*)(-*A[A-]*)'
, which works as long as you only pass one argument. Saysubpositional a
, as you require at least one positional argument in the second group. Again, as the first group is greedy, passingsubpositional a b c d
gets you['AAAA', 'A']
, which is not what you wanted.In brief: a mess. I guess this should be considered a bug, but not sure what the impact would be if the patterns are turned into non-greedy ones...
常见的做法是命令之前(左侧)的参数属于主程序,命令之后(右侧)的参数属于命令。因此,
positional
应该位于命令subpositional
之前。示例程序:git
、twistd
。此外,带有
narg=?
的参数可能应该是一个选项 (--opt=value
),而不是位置参数。The common practice is that arguments before the command (on the left side) belong to the main program, after (on the right) -- to the command. Therefore
positional
should go before the commandsubpositional
. Example programs:git
,twistd
.Additionally an argument with
narg=?
should probably be an option (--opt=value
), and not a positional argument.我认为问题在于,当调用 add_subparsers 时,会向原始解析器添加一个新参数以传递子解析器的名称。
例如,使用以下代码:
您将获得以下帮助字符串:
请注意,
subpositional
显示在positional
之前。我想说你要寻找的是在子解析器名称之前有位置参数。因此,您可能正在寻找的是在子解析器之前添加参数:使用此代码获得的帮助字符串是:
这样,您首先将参数传递给主解析器,然后传递子解析器的名称,最后传递给主解析器的参数子解析器(如果有)。
I think that the problem is that when
add_subparsers
is called, a new parameter is added to the original parser to pass the name of the subparser.For example, with this code:
You get the following help string:
Note that
subpositional
is displayed beforepositional
. I'd say that what you're looking for is to have the positional argument before the subparser name. Hence, probably what you're looking for is adding the argument before the subparsers:The help string obtained with this code is:
This way, you pass first the arguments to the main parser, then the name of the subparser and finally the arguments to the subparser (if any).
Python 3.5 中仍然是一团糟。
我建议子类 ArgumentParser 保留所有剩余的位置参数,并稍后处理它们:
剩余的位置参数在列表
options.remaining_positionals
中It's still a mess in Python 3.5.
I suggest to subClass ArgumentParser to keep all the remaining positional arguments, and to deal with them later:
The remaining positional arguments are in the list
options.remaining_positionals