Python 如何让argparse选择默认的子parser?

Python 如何让argparse选择默认的子parser?,python,argparse,Python,Argparse,我在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_parse

我在
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)
我可以称之为三种不同的方式:

$ python3 script.py
Namespace(command='a')
$ python3 script.py a
Namespace(command='a', thing='thing')
$ python3 script.py b
Namespace(command='b', nothing='nothing')
这只存在一个问题:我想要的是,如果我在命令行上提供零参数,那么
a_解析器
将成为最终解析和执行任务的解析器。显然不是这样,
sp.default
只是设置了
command='a'
,而不是我所期望的,也就是说,“哦,是的,用户没有在命令行上提供任何参数,但我知道这应该由
a_解析器处理。
。这里是
名称空间(command='a',thing='thing')
!”

有没有一种方法可以用argparse实现这一点?我已经寻找了一些不同的选择,但没有一个能真正提供我想要的。我想我可以制作3个不同的ArgumentParser,然后将参数传递给它们中的每一个,尽管这听起来有点恶心


有更好的选择吗?

首先是一个历史注释-子parser不是可选的,它们仍然不在Python2中。在Py3中它们是可选的,这是几年前引入的一个bug。对所需参数的测试发生了变化,次parser(一种位置)从裂缝中消失。如果操作正确,您应该将子Parser显式设置为非必需

子参数的行为与其他非必需参数不同,这些参数带有
nargs='?'
或标记为不带
required
参数

在任何情况下,您的
sp.default
定义将放入
命令中的值
dest,但它不会触发使用
a_解析器
。该
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]: []
然后有条件地运行一个解析器(如果命令是'a'但不是'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')
它试图将“哎哟”解析为子parser名称。主解析器对
--thing
参数一无所知

正如我在今天的另一个
argparse
问题中所解释的,顶级解析器解析输入,直到找到符合“subparsers”命令的内容(或者在本例中引发错误)。然后它将解析传递给子parser。之后,它不会恢复解析

我对这个Py2请求的回答可能适合您。我首先用一个没有子parser的解析器运行一个
parse\u known\u args
,然后有条件地运行第二个处理子parser的解析器

In [165]: firstp = argparse.ArgumentParser()
In [166]: args, extras = firstp.parse_known_args('--thing ouch'.split())
In [167]: args
Out[167]: Namespace()
如果
extras
没有'a'或'b'调用
a_解析器
(或者直接查看
sys.argv[1://code>):

或者修改
extras
以包含缺少的subparser命令:

In [170]: extras = ['a']+extras
In [171]: parser.parse_args(extras)
Out[171]: Namespace(command='a', thing='ouch')

在任何情况下,
argparse
中没有很好地开发
optional
子parser。这是前一段时间所做更改的副作用,而不是经过深思熟虑的功能。

您可以研究像或这样的命令行框架,它们通常为此类设计提供更好的支持。更好地使用docopt
In [168]: extras
Out[168]: ['--thing', 'ouch']
In [169]: a_parser.parse_args(extras)
Out[169]: Namespace(thing='ouch')
In [170]: extras = ['a']+extras
In [171]: parser.parse_args(extras)
Out[171]: Namespace(command='a', thing='ouch')