@Vikas提供的解决方案对于特定于子命令的可选参数失败,但是该方法有效。这是一个改进的版本:
import argparse
parser = argparse.ArgumentParser(prog='PROG')
parser.add_argument('--foo', action='store_true', help='foo help')
subparsers = parser.add_subparsers(help='sub-command help', dest='subparser_name')
parser_a = subparsers.add_parser('command_a', help='command_a help')
parser_a.add_argument('bar', type=int, help='bar help')
parser_b = subparsers.add_parser('command_b', help='command_b help')
parser_b.add_argument('--baz', choices='XYZ', help='baz help')
argv = ['--foo', 'command_a', '12', 'command_b', '--baz', 'Z']
while argv:
print(argv)
options, argv = parser.parse_known_args(argv)
print(options)
if not options.subparser_name:
break
使用parse_known_args
代替parse_args
。parse_args
遇到当前子解析器未知的参数时,异常终止,parse_known_args
将其作为返回的元组中的第二个值返回。在这种方法中,剩余的参数将再次馈送到解析器。因此,对于每个命令,都会创建一个新的命名空间。
请注意,在此基本示例中,所有全局选项仅添加到第一个选项名称空间,而不添加到后续名称空间。
这种方法在大多数情况下都可以正常工作,但有三个重要限制:
- 不能将相同的可选参数用于不同的子命令,例如
myprog.py command_a --foo=bar command_b --foo=bar
。
- 这不是可以使用任何可变长度位置参数与子命令(
nargs='?'
或nargs='+'
或nargs='*'
)。
- 解析任何已知参数,而不会在新命令中“中断”。例如,
PROG --foo command_b command_a --baz Z 12
上面的代码--baz Z
将由而command_b
不是由消耗command_a
。
这些限制是argparse的直接限制。这是一个简单的示例,显示了argparse的局限性-即使在使用单个子命令时也是如此:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('spam', nargs='?')
subparsers = parser.add_subparsers(help='sub-command help', dest='subparser_name')
parser_a = subparsers.add_parser('command_a', help='command_a help')
parser_a.add_argument('bar', type=int, help='bar help')
parser_b = subparsers.add_parser('command_b', help='command_b help')
options = parser.parse_args('command_a 42'.split())
print(options)
这将提高error: argument subparser_name: invalid choice: '42' (choose from 'command_a', 'command_b')
。
原因是内部方法argparse.ArgParser._parse_known_args()
过于贪婪,并假定这command_a
是可选spam
参数的值。特别是,当“拆分”可选参数和位置参数时,_parse_known_args()
它不会查看修饰符的名称(如command_a
或command_b
),而只会查看它们在参数列表中的位置。它还假定任何子命令都将使用所有剩余的参数。这种限制argparse
还妨碍了多命令子解析器的正确实现。不幸的是,这意味着正确的实现需要完全重写该argparse.ArgParser._parse_known_args()
方法,即200多个代码行。
考虑到这些限制,可以选择简单地还原为单个多项选择参数而不是子命令:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--bar', type=int, help='bar help')
parser.add_argument('commands', nargs='*', metavar='COMMAND',
choices=['command_a', 'command_b'])
options = parser.parse_args('--bar 2 command_a command_b'.split())
print(options)
甚至有可能在使用信息中列出不同的命令,请参阅我的答案https://stackoverflow.com/a/49999185/428542