我正在使用argparse,我正在尝试混合子命令和位置参数,并出现了以下问题。
此代码运行良好:
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')
,但是当我将position参数更改为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
为什么会这样?
答案 0 :(得分:9)
起初我认为和jcollado一样,但事实上,如果后续(顶级)位置参数具有特定nargs
(nargs
= None
,{ {1}} =整数),然后它按预期工作。它在nargs
为nargs
或'?'
时失败,有时在'*'
时失败。所以,我去了解代码,找出发生了什么。
归结为参数被拆分的方式。为了弄清楚谁得到了什么,对'+'
的调用总结了像parse_args
这样的字符串中的参数,在你的情况下('AA'
表示位置参数,'A'
表示可选),并最终生成一个与该摘要字符串匹配的正则表达式模式,具体取决于您通过'O'
和.add_argument
方法添加到解析器的操作。
在每种情况下,对于您的示例,参数字符串最终为.add_subparsers
。要匹配的模式有哪些更改(您可以在'AA'
中查看_get_nargs_pattern
下的可能模式。对于argparse.py
,它最终为subpositional
,这意味着允许一个参数后跟任意数量的选项或参数。对于'(-*A[-AO]*)'
,它取决于传递给positional
的值:
nargs
=> None
'(-*A-*)'
(每个预期参数一个'(-*A-*A-*A-*)'
)'-*A'
=> '?'
'(-*A?-*)'
=> '*'
'(-*[A-]*)'
=> '+'
附加了这些模式,对于'(-*A[A-]*)'
(您的工作示例),您最终得到的nargs=None
与两个组'(-*A[-AO]*)(-*A-*)'
相匹配。这样,['A', 'A']
将仅解析subpositional
(您想要的内容),而subpositional
将匹配其操作。
但对于positional
,您最终会得到nargs='?'
。第二组完全由可选模式组成,'(-*A[-AO]*)(-*A?-*)'
贪婪,这意味着第一组将字符串中的所有内容全局化,最终识别出两组*
。这意味着['AA', '']
得到两个参数,当然最终会窒息。
有趣的是,subpositional
的模式是nargs='+'
,只要您传递一个参数,它就可以使用。说'(-*A[-AO]*)(-*A[A-]*)'
,因为您需要在第二组中至少有一个位置参数。同样,由于第一组是贪婪的,通过subpositional a
会得到subpositional a b c d
,这不是你想要的。
简而言之:一团糟。我想这应该被视为一个错误,但不确定如果模式变成非贪婪的那个会产生什么影响...
答案 1 :(得分:6)
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('positional', nargs='?')
subparsers = parser.add_subparsers()
subparsers.add_parser('subpositional')
print(parser.parse_args(['positional', 'subpositional']))
# -> Namespace(positional='positional')
print(parser.parse_args(['subpositional']))
# -> Namespace(positional=None)
parser.print_usage()
# -> usage: bpython [-h] [positional] {subpositional} ...
通常的做法是命令之前的参数(在左侧)属于主程序,在(在右侧)之后属于命令。因此positional
应该在命令subpositional
之前。示例程序:git
,twistd
。
此外,narg=?
的参数应该是一个选项(--opt=value
),而不是位置参数。
答案 2 :(得分:5)
我认为问题在于,当调用add_subparsers
时,会向原始解析器添加一个新参数以传递子分析器的名称。
例如,使用以下代码:
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
parser.add_argument('positional')
subparsers.add_parser('subpositional')
parser.parse_args()
您将获得以下帮助字符串:
usage: test.py [-h] {subpositional} ... positional
positional arguments:
{subpositional}
positional
optional arguments:
-h, --help show this help message and exit
请注意,subpositional
之前会显示positional
。我要说的是你要找的是在subparser名称之前有位置参数。因此,您可能正在寻找的是在subparsers之前添加参数:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('positional')
subparsers = parser.add_subparsers()
subparsers.add_parser('subpositional')
parser.parse_args()
使用此代码获取的帮助字符串是:
usage: test.py [-h] positional {subpositional} ...
positional arguments:
positional
{subpositional}
optional arguments:
-h, --help show this help message and exit
这样,你首先将参数传递给主解析器,然后传递subparser的名称,最后传递subparser的参数(如果有的话)。
答案 3 :(得分:0)
Python 3.5仍然是一团糟。
我建议subClass ArgumentParser保留所有剩余的位置参数,并在以后处理它们:
import argparse
class myArgumentParser(argparse.ArgumentParser):
def parse_args(self, args=None, namespace=None):
args, argv = self.parse_known_args(args, namespace)
args.remaining_positionnals = argv
return args
parser = myArgumentParser()
options = parser.parse_args()
其余的位置参数位于列表options.remaining_positionals