Python argparse 位置参数和子命令
我正在使用 argparse 并尝试混合使用子命令和位置参数,但出现了以下问题.
I'm working with argparse and am trying to mix sub-commands and positional arguments, and the following issue came up.
此代码运行良好:
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
parser.add_argument('positional')
subparsers.add_parser('subpositional')
parser.parse_args('subpositional positional'.split())
上面的代码将 args 解析为 Namespace(positional='positional')
,但是当我将位置参数更改为具有 nargs='?'像这样:
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())
它出错了:
usage: [-h] {subpositional} ... [positional]
: error: unrecognized arguments: positional
这是为什么?
起初我认为与 jcollado 相同,但事实是,如果后续(顶级)位置参数具有特定的 nargs
(nargs
= None
, nargs
= integer),然后它会按您的预期工作.当 nargs
是 '?'
或 '*'
时,它会失败,有时当它是 '+'
时.所以,我深入研究了代码,想弄清楚发生了什么.
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 when nargs
is '?'
or '*'
, and sometimes when it is '+'
. So, I went down to the code, to figure out what is going on.
归结为参数被拆分以供使用的方式.为了弄清楚谁得到了什么,对 parse_args
的调用总结了一个字符串中的参数,如 'AA'
,在你的情况下 ('A'
对于位置参数,'O'
为可选),并最终生成一个正则表达式模式以与该摘要字符串匹配,具体取决于您通过 添加到解析器的操作.add_argument
和 .add_subparsers
方法.
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.
在每种情况下,例如,参数字符串最终都是 'AA'
.改变的是要匹配的模式(你可以在 argparse.py
中的 _get_nargs_pattern
下看到可能的模式.对于 subpositional
,它最终是 _get_nargs_pattern
code>'(-*A[-AO]*)',这意味着允许一个参数后跟任意数量的选项或参数.对于位置
,这取决于传递给 nargs
的值:
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
in argparse.py
. For subpositional
it ends up being '(-*A[-AO]*)'
, which means allow one argument followed by any number of options or arguments. For positional
, it depends on the value passed to nargs
:
-
无
=>'(-*A-*)'
- 3 =>
'(-*A-*A-*A-*)'
(每个预期参数一个'-*A'
) -
'?'
=>'(-*A?-*)'
-
'*'
=>'(-*[A-]*)'
-
'+'
=>'(-*A[A-]*)'
-
None
=>'(-*A-*)'
- 3 =>
'(-*A-*A-*A-*)'
(one'-*A'
per expected argument) -
'?'
=>'(-*A?-*)'
-
'*'
=>'(-*[A-]*)'
-
'+'
=>'(-*A[A-]*)'
附加这些模式,对于 nargs=None
(您的工作示例),您最终会得到 '(-*A[-AO]*)(-*A-*)'
,匹配两组['A', 'A']
.这样,subpositional
将只解析 subpositional
(你想要的),而 positional
将匹配它的动作.
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 only subpositional
(what you wanted), while positional
will match its action.
但是,对于 nargs='?'
,您最终会得到 '(-*A[-AO]*)(-*A?-*)'
.第二组完全由 optional 模式组成,并且 *
是贪婪的,这意味着第一组对字符串中的所有内容进行 glob,最终识别出这两组 ['AA', '']
.这意味着 subpositional
获得两个参数,当然最终会窒息.
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 means subpositional
gets two arguments, and ends up choking, of course.
有趣的是,nargs='+'
的模式是 '(-*A[-AO]*)(-*A[A-]*)'
,只要你只传递一个参数.说 subpositional a
,因为您需要在第二组中至少有一个位置参数.同样,由于第一组是贪婪的,传递 subpositional a b c d
会得到 ['AAAA', 'A']
,这不是你想要的.
Funny enough, the pattern for nargs='+'
is '(-*A[-AO]*)(-*A[A-]*)'
, which works as long as you only pass one argument. Say subpositional a
, as you require at least one positional argument in the second group. Again, as the first group is greedy, passing subpositional 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...