使用parse_known_args解析多个子命令出错,如何实现预期效果?
如何使用argparse的parse_known_args解析多个子命令?
我尝试用Python的argparse模块通过parse_known_args同时解析多个子命令,但得到的结果不符合预期。以下是我的代码:
import argparse parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(help='config command help') # create parser for config command parser_config = subparsers.add_parser('config', help='config help') parser_config.add_argument('--configname', required=True, nargs=1) parser_config.add_argument('--login', required=True, nargs=1) parser_config.add_argument('--password', required=True, nargs=1) parser_config1 = subparsers.add_parser('config1', help='config help') parser_config1.add_argument('--configname', required=True, nargs=1) parser_config1.add_argument('--login', required=True, nargs=1) parser_config1.add_argument('--password', required=True, nargs=1) rest = 'config --configname name1 --login abc1 --password def1 config1 --configname name2 --login abc2 --password def2'.split() print(rest) while rest: args,rest = parser.parse_known_args(rest) argslist.append(args) print(args, rest)
当前运行输出:
$ python3 create_config.py ['config', '--configname', 'name1', '--login', 'abc1', '--password', 'def1', 'config1', '--configname', 'name2', '--login', 'abc2', '--password', 'def2'] Namespace(configname=['name2'], login=['abc2'], password=['def2']) ['config1'] [Namespace(configname=['name2'], login=['abc2'], password=['def2'])] usage: create_config.py config1 [-h] --configname CONFIGNAME --login LOGIN --password PASSWORD create_config.py config1: error: the following arguments are required: --configname, --login, --password
我期望的输出是:
['config', '--configname', 'name1', '--login', 'abc1', '--password', 'def1', 'config1', '--configname', 'name2', '--login', 'abc2', '--password', 'def2'] Namespace(configname=['name1'], login=['abc1'], password=['def1']) ['config1', '--configname', 'name2', '--login', 'abc2', '--password', 'def2'] Namespace(configname=['name2'], login=['abc2'], password=['def2']) []
请问该如何实现这一预期效果?
问题出在哪?
这是因为parse_known_args的默认逻辑:当它开始解析第一个子命令(比如config)时,会把后面所有的可选参数(像--configname、--login这类带--的参数)都当成当前子命令的参数。而且如果重复传入同一个可选参数,argparse会用最后一次的值覆盖之前的,所以你第一次解析得到的是最后一组configname、login的值。同时,它把下一个子命令config1当成了config子命令的未知参数,丢进了rest里,导致第二次解析时config1没有对应的参数,直接报错。
解决方案
我们需要手动分割参数列表,每次只解析到下一个子命令之前的部分。具体步骤是:
- 先获取所有子命令的名称,方便识别下一个子命令的位置
- 在每次循环中,找到下一个子命令的索引,将当前参数列表分割为当前子命令的参数和剩余参数
- 解析当前子命令的参数,继续循环处理剩余部分
修改后的代码如下:
import argparse parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(help='config command help') # create parser for config command parser_config = subparsers.add_parser('config', help='config help') parser_config.add_argument('--configname', required=True, nargs=1) parser_config.add_argument('--login', required=True, nargs=1) parser_config.add_argument('--password', required=True, nargs=1) parser_config1 = subparsers.add_parser('config1', help='config help') parser_config1.add_argument('--configname', required=True, nargs=1) parser_config1.add_argument('--login', required=True, nargs=1) parser_config1.add_argument('--password', required=True, nargs=1) rest = 'config --configname name1 --login abc1 --password def1 config1 --configname name2 --login abc2 --password def2'.split() print(rest) argslist = [] # 初始化argslist,避免报错 subcmd_names = list(subparsers.choices.keys()) # 获取所有子命令名称 while rest: # 寻找下一个子命令的位置 next_subcmd_idx = None for i in range(1, len(rest)): if rest[i] in subcmd_names: next_subcmd_idx = i break # 分割参数列表 if next_subcmd_idx is not None: current_args = rest[:next_subcmd_idx] rest = rest[next_subcmd_idx:] else: current_args = rest rest = [] # 解析当前子命令的参数 args, unknown = parser.parse_known_args(current_args) argslist.append(args) print(args, rest) print(argslist) # 可选:打印最终收集的所有args
代码解释
subcmd_names = list(subparsers.choices.keys()):获取所有已定义的子命令名称(这里是['config', 'config1'])- 循环遍历剩余参数,找到第一个属于子命令名称的元素位置,以此为界分割参数,确保当前只解析第一个子命令及其专属参数
- 分割后,
current_args包含当前子命令的所有参数,rest则是剩余的待解析参数 - 每次解析完成后,将结果存入
argslist,继续处理剩余参数直到为空
运行修改后的代码,就能得到你期望的输出了。
内容的提问来源于stack exchange,提问作者Ankur Agarwal
相关产品推荐
相关产品推荐

