Python argparse 中的 REMAINDER 不明确
根据文档的说明:
argparse.REMAINDER。所有剩下的命令行参数会被收集到一个列表中。这在一些命令行工具中很有用,因为它们需要把参数传递给其他命令行工具:
>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('--foo')
>>> parser.add_argument('command')
>>> parser.add_argument('args', nargs=argparse.REMAINDER)
>>> print parser.parse_args('--foo B cmd --arg1 XX ZZ'.split())
Namespace(args=['--arg1', 'XX', 'ZZ'], command='cmd', foo='B')
我试着用这个来达到同样的目的,但在某些情况下,它对我来说似乎有点问题 (或者我可能理解错了这个概念):
import argparse
a = argparse.ArgumentParser()
a.add_argument('-qa', nargs='?')
a.add_argument('-qb', nargs='?')
a.add_argument('rest', nargs=argparse.REMAINDER)
a.parse_args('-qa test ./otherutil bar -q atr'.split())
结果:
test.py: error: ambiguous option: -q could match -qa, -qb
所以显然,如果 otherutil
有一些参数和传给 argparse
的参数“冲突”,它似乎就不能正常工作了。
我本来期待当 argparse
遇到 REMAINDER
这种参数时,它会直接把列表末尾的所有字符串都用上,而不再进行进一步的解析。我能以某种方式实现这个效果吗?
4 个回答
你需要使用两个 --
。
a.add_argument('--qa', nargs='?')
a.add_argument('--qb', nargs='?')
这样你定义的选项就不会和一个叫 -q
的选项冲突,后者需要至少一个参数,这个参数是在别的地方定义的。
来自 argparse 文档
ArgumentParser.add_argument(name or flags...)
name or flags - Either a name or a list of option strings, e.g. foo or -f, --foo.
编辑:回复 @PDani 的第一个评论:
这篇帖子很有意思。
根据我的理解,argparse 遵循 POSIX 和 GNU 的风格。
一个重要的点是,短选项(一个字母的选项)可以组合在一起,如果某个选项需要一个参数,这个参数可以直接跟在选项字母后面。例如,如果你有这样的情况:
a.add_argument('-a', action='store_true')
a.add_argument('-b', action='store_true')
a.add_argument('-c', action='store_true')
a.add_argument('-d', nargs=1)
a.add_argument('-e', nargs=1)
你可以这样调用它们 -abcd3 -e5
或者 -a -b -c -d3 -e5
或者 -cba -e5 -d3
,等等。
现在,如果你有
a.add_argument('-abc', action='store_true')
那么对于 argparse 来说,很难判断 -abc
是三个短选项组合在一起,还是一个长选项。所以你必须把参数定义为 --abc
。
所以我想你不能用一个 -
来表示长选项名。
我知道还有一种替代的命令行解析方式,叫做 docopt:你可以看看,但我怀疑它能解决你的问题。
这段内容主要讲的是如何处理缩写,而不是关于 REMAINDER nargs
的问题。
In [111]: import argparse
In [112]: a = argparse.ArgumentParser()
...:
...: a.add_argument('-qa', nargs='?')
...: a.add_argument('-qb', nargs='?')
In [113]: a.parse_args('-qa test ./otherutil bar -q atr'.split())
usage: ipython3 [-h] [-qa [QA]] [-qb [QB]]
ipython3: error: ambiguous option: -q could match -qa, -qb
argparse
这个工具会进行两次解析。第一次,它会尝试把输入的字符串分成选项(标志)和参数。第二次,它会交替解析位置参数和可选参数,并根据 nargs
来分配参数。
这里的模糊性发生在第一次解析时。它正在尝试将 '-q' 匹配到两个可用的选项中。而 REMAINDER 的特殊处理(把 '-q' 当作普通字符串来处理)是在第二次解析时才会发生。
更新版的 argparse
允许我们关闭缩写处理:
In [114]: a.allow_abbrev
Out[114]: True
In [115]: a.allow_abbrev=False
In [116]: a.parse_args('-qa test ./otherutil bar -q atr'.split())
usage: ipython3 [-h] [-qa [QA]] [-qb [QB]]
ipython3: error: unrecognized arguments: ./otherutil bar -q atr
如果我添加 REMAINDER 动作:
In [117]: a.add_argument('rest', nargs=argparse.REMAINDER)
In [118]: a.parse_args('-qa test ./otherutil bar -q atr'.split())
Out[118]: Namespace(qa='test', qb=None, rest=['./otherutil', 'bar', '-q', 'atr'])
使用 '--' 就像 @Colin 建议的那样是有效的,因为这个字符串在第一次解析时就被识别了:
In [119]: a.allow_abbrev=True
In [120]: Out[117].nargs='*'
In [121]: a.parse_args('-qa test -- ./otherutil bar -q atr'.split())
Out[121]: Namespace(qa='test', qb=None, rest=['./otherutil', 'bar', '-q', 'atr'])
我在尝试把选项传递给一个底层工具时遇到了这个问题。最后我使用的解决方案是 nargs='*'
,而不是 nargs=argparse.REMAINDER
。然后我用一个“伪参数” --
来分隔我命令的选项和底层工具的选项:
>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--myflag', action='store_true')
>>> parser.add_argument('toolopts', nargs='*')
>>> parser.parse_args('--myflag -- -a --help'.split())
Namespace(myflag=True, toolopts=['-a', '--help'])
这样在帮助输出中记录起来也比较简单。