我希望我的程序允许任何字符串作为参数传递,但也允许子命令。例如,这些都是有效的:
$ python argtest.py 'hello world'
$ python argtest.py version
$ python argtest.py help
我不希望用户必须使用
--
,例如 python argparse.py -- 'hello world'
。
如果我设置一个子命令解析器来处理“版本”和“帮助”,它会抱怨“hello world”不是有效的子命令。
我的代码:
parser = argparse_flags.ArgumentParser()
subparsers = parser.add_subparsers(required=False)
subparsers.add_parser('version')
subparsers.add_parser('help')
ns, remainder = parser.parse_known_args(argv[1:])
产量:
$ python argtest.py 'hello world'
usage: argtest.py [-h] {version,help} ...
argtest.py: error: invalid choice: 'hello world' (choose from 'version', 'help')
我想出的唯一修复方法是创建一个“后备”子命令,然后通过以下任一方式使用
parser.parse_known_args(['fallback'] + argv[1:])
手动添加它:
subparsers.choices
中。所有这些都有点恶心,有没有更好的方法来获得我想要的行为?我只是希望 argparse 对于找不到子命令更加冷静。
add_subparsers
是 add_argument
的变体,使用 Action
创建 nargs=argparse.PARSER
子类。
只有
optionals
才能按值选择其 flag
字符串。位置严格按照职位和nargs
分配。这是在 _get_values
中完成的。对于解析器
# PARSER arguments convert all values, but check only the first
elif action.nargs == PARSER:
value = [self._get_value(action, v) for v in arg_strings]
self._check_value(action, value[0])
因此
arg_strings
中所有剩余的字符串都被分配。 _get_value
针对 type
进行测试,这里只是通过 str
。 _check_value
对照 choices
测试该列表中的第一个。
对于子解析器,
choices
是子解析器名称及其别名。所以这就是你的“hello_world”失败的地方。
正如您所注意到的,可以捕获退出错误。最近添加了一个
exit_on_error
参数,该参数将引发错误而不是退出。它并非在所有情况下都有效,但我认为它在这里应该有效。正如文档所述,您还可以调整 error
和 exit
方法来更改引发错误的方式。
通常,仅当
positional
为“?”时,才不需要 nargs
或者 '*'。由于旧补丁的调整,还有这个“PARSER”案例。如果字符串失败 type
或 choices
,则没有机制可以重试分配字符串。
如果有多个
positionals
,则以 re
贪婪的方式分配字符串。第一个参数可以获取尽可能多的值,等等。因此,在字符串“+*?”时要小心nargs
在一起。
总之,我认为如果为主解析器定义一堆
optionals
,然后定义 add_subparsers
操作,子解析器机制效果最好。这样它就可以处理 optionals
(如果有),然后分支到所选的子解析器来处理其余部分。可以为主解析器定义 positional
,但避免开放式 nargs
。
子解析器的另一件事。在主解析器和子解析器中使用不同的参数
dest
。这样子解析器默认值就不会踩在主解析器默认值上。