我有以下代码script.py:
import argparse
parser = argparse.ArgumentParser()
sp = parser.add_subparsers(dest='command')
sp.default = 'a'
a_parser = sp.add_parser('a')
b_parser = sp.add_parser('b')
a_parser.add_argument('--thing', default='thing')
b_parser.add_argument('--nothing', default='nothing')
args = parser.parse_args()
print(args)
Run Code Online (Sandbox Code Playgroud)
我可以称之为三种不同的方式:
$ python3 script.py
Namespace(command='a')
$ python3 script.py a
Namespace(command='a', thing='thing')
$ python3 script.py b
Namespace(command='b', nothing='nothing')
Run Code Online (Sandbox Code Playgroud)
这只有一个问题:我想要的是,如果我在命令行上提供零个参数是为了a_parser成为结束解析和做事的那个。显然不是,这sp.default只是设置command='a',不是我所期望的,也就是说,“哦,是的,用户没有在命令行上提供任何参数,但我知道这应该由 处理a_parser。这里是Namespace(command='a', thing='thing')!”
有没有一种方法,我可以用argparse做到这一点?我已经寻找了一些不同的选择,但它们似乎都没有真正提供我所追求的。我想我可以通过制作 3 个不同的 ArgumentParsers 然后将参数传递给每个人来做一些麻烦事,尽管这听起来有点恶心。
还有更好的选择吗?
首先是历史记录 - 子解析器不是可选的,并且它们仍然不在 Python2 中。事实上,它们在 Py3 中是可选的,这是几年前引入的一个错误。所需参数的测试发生了变化,子解析器(一种位置)被排除在外。如果做得正确,您应该必须显式地将子解析器设置为不需要。
子解析器的行为与其他非必需参数(带参数nargs='?'或不带required参数标记的参数)不同。
无论如何,您sp.default定义了将放入commanddest 的值,但它不会触发 的使用a_parser。这command='a'永远不会被“评估”。
使用parse_known_args可能允许您使用 来贬值剩余的字符串a_parser。
没有任何参数,我们可以这样做:
In [159]: args, extras = parser.parse_known_args([])
In [160]: args
Out[160]: Namespace(command='a')
In [161]: extras
Out[161]: []
Run Code Online (Sandbox Code Playgroud)
然后有条件地运行a_parser(如果命令是“a”但没有“thing”)
In [163]: a_parser.parse_args(extras,namespace=args)
Out[163]: Namespace(command='a', thing='thing')
Run Code Online (Sandbox Code Playgroud)
但如果我尝试包含一个--thing值:
In [164]: args, extras = parser.parse_known_args('--thing ouch'.split())
usage: ipython3 [-h] {a,b} ...
ipython3: error: argument command: invalid choice: 'ouch' (choose from 'a', 'b')
Run Code Online (Sandbox Code Playgroud)
它尝试将“哎呀”解析为子解析器名称。主解析器对参数一无所知--thing。
正如我今天在另一个argparse问题中解释的那样,顶级解析器解析输入,直到找到适合“子解析器”命令的内容(或者在本示例中引发错误)。然后它将解析传递给子解析器。之后它不会恢复解析。
如何使用 Python 2.7 的 Argparse 模块设置默认子解析器
我对此 Py2 请求的回答可能对您有用。我首先运行一个parse_known_args没有子解析器的解析器,然后有条件地运行第二个解析器来处理子解析器。
In [165]: firstp = argparse.ArgumentParser()
In [166]: args, extras = firstp.parse_known_args('--thing ouch'.split())
In [167]: args
Out[167]: Namespace()
Run Code Online (Sandbox Code Playgroud)
如果extras没有 'a' 或 'b' 调用a_parser(或者直接查看sys.argv[1:]):
In [168]: extras
Out[168]: ['--thing', 'ouch']
In [169]: a_parser.parse_args(extras)
Out[169]: Namespace(thing='ouch')
Run Code Online (Sandbox Code Playgroud)
或者修改extras以包含缺少的子解析器命令:
In [170]: extras = ['a']+extras
In [171]: parser.parse_args(extras)
Out[171]: Namespace(command='a', thing='ouch')
Run Code Online (Sandbox Code Playgroud)
无论如何,optional子解析器在argparse. 这是不久前所做的更改的副作用,而不是经过深思熟虑的功能。