Python argparse 位置参数和子命令
Posted
技术标签:
【中文标题】Python argparse 位置参数和子命令【英文标题】:Python argparse positional arguments and sub-commands 【发布时间】:2012-01-29 21:50:55 【问题描述】:我正在使用 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')
,但是当我将位置参数更改为具有 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
这是为什么?
【问题讨论】:
顺便说一句,似乎是known bug,已针对最近的 Python 版本进行了修复。 【参考方案1】:起初我的想法和 jcollado 一样,但事实是,如果后续(***)位置参数具有特定的 nargs
(nargs
= None
,nargs
= 整数),然后它会按您的预期工作。当nargs
为'?'
或'*'
时失败,有时为'+'
。所以,我深入到代码中,想弄清楚发生了什么。
归结为参数被拆分以供使用的方式。为了弄清楚谁得到了什么,对parse_args
的调用将参数汇总在一个字符串中,如'AA'
,在你的情况下('A'
用于位置参数,'O'
用于可选),并最终生成一个正则表达式模式与该摘要字符串匹配,具体取决于您通过 .add_argument
和 .add_subparsers
方法添加到解析器的操作。
在每种情况下,例如,参数字符串最终都是'AA'
。要匹配的模式有什么变化(您可以在argparse.py
中的_get_nargs_pattern
下看到可能的模式。对于subpositional
,它最终是'(-*A[-AO]*)'
,这意味着允许一个参数后跟任意数量的选项或参数。对于positional
,它取决于传递给nargs
的值:
None
=> '(-*A-*)'
3 => '(-*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
得到['AAAA', 'A']
,这不是你想要的。
简而言之:一团糟。我想这应该被认为是一个错误,但不确定如果模式变成非贪婪模式会产生什么影响......
【讨论】:
请注意,当然,按照 jcollado 和 argparses 文档的建议,在所有顶层之后添加子解析器将打破歧义并按预期工作!【参考方案2】: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
),而不是位置参数。
【讨论】:
如果子解析器有位置参数怎么办?我们怎样才能让print(parser.parse_args(['subpositional', 'subparserarg']))
打印:# -> Namespace(positional=None)
?这应该说我们正在选择子命令“subpositional”,参数positional
是可选的。这可能吗?【参考方案3】:
我认为问题是在调用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
之前。我会说你正在寻找的是在子解析器名称之前有位置参数。因此,您可能正在寻找的是在子解析器之前添加参数:
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
这样,您首先将参数传递给主解析器,然后是子解析器的名称,最后是子解析器的参数(如果有的话)。
【讨论】:
不幸的是,它似乎不起作用。帮助确实看起来应该如此,但在实践中 - 它不会改变解析过程。【参考方案4】:在 Python 3.5 中仍然一团糟。
我建议子类 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
【讨论】:
以上是关于Python argparse 位置参数和子命令的主要内容,如果未能解决你的问题,请参考以下文章
python-argparse.ArgumentParser()用法解析