我有以下测试代码
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbose", default = 0, type=int)
subparsers = parser.add_subparsers(dest = "parser_name")
parser_lan = subparsers.add_parser('car')
parser_lan.add_argument("--boo")
parser_lan.add_argument("--foo")
parser_serial = subparsers.add_parser('bus')
parser_serial.add_argument("--fun")
print parser.parse_args()
,它定义了两个子解析器,具有不同的参数集。当我调用testcode为
时tester.py --verbose 3 car --boo 1 --foo 2
我得到了预期的结果
Namespace(boo='1', foo='2', parser_name='car', verbose=3)
我想要的是每个子解析器的值放在一个单独的命名空间或字典中,比如
Namespace(subparseargs={boo:'1', foo:'2'}, parser_name='car', verbose=3)
使得来自每个子解析器的参数与来自主解析器的参数在逻辑上分离(如本例中的verbose
)。
如何实现这一点,使用相同命名空间(示例中的subparseargs
)中的每个子解析器的参数。
您需要进入argparse
的内部,但将脚本更改为以下内容应该可以达到目的:
import argparse
from argparse import _HelpAction, _SubParsersAction
class MyArgumentParser(argparse.ArgumentParser):
def parse_args(self, *args, **kw):
res = argparse.ArgumentParser.parse_args(self, *args, **kw)
from argparse import _HelpAction, _SubParsersAction
for x in parser._subparsers._actions:
if not isinstance(x, _SubParsersAction):
continue
v = x.choices[res.parser_name] # select the subparser name
subparseargs = {}
for x1 in v._optionals._actions: # loop over the actions
if isinstance(x1, _HelpAction): # skip help
continue
n = x1.dest
if hasattr(res, n): # pop the argument
subparseargs[n] = getattr(res, n)
delattr(res, n)
res.subparseargs = subparseargs
return res
parser = MyArgumentParser()
parser.add_argument("--verbose", default = 0, type=int)
subparsers = parser.add_subparsers(dest = "parser_name")
parser_lan = subparsers.add_parser('car')
parser_lan.add_argument("--boo")
parser_lan.add_argument("--foo")
parser_serial = subparsers.add_parser('bus')
parser_serial.add_argument("--fun")
print parser.parse_args()
我已经开始开发一种不同的方法(但类似于Anthon的建议),并提出了一个更短的代码。然而,我不确定我的方法是这个问题的通用解决方案。
与Anthon提议的相似,我定义了一个新方法,该方法创建了一个保存在args
中的"顶级"参数列表,而所有其他参数都作为额外的字典返回:
class MyArgumentParser(argparse.ArgumentParser):
def parse_subargs(self, *args, **kw):
# parse as usual
args = argparse.ArgumentParser.parse_args(self, *args, **kw)
# extract the destination names for top-level arguments
topdest = [action.dest for action in parser._actions]
# loop over all arguments given in args
subargs = {}
for key, value in args.__dict__.items():
# if sub-parser argument found ...
if key not in topdest:
# ... remove from args and add to dictionary
delattr(args,key)
subargs[key] = value
return args, subargs
欢迎对这种方法的评论,特别是我忽略的任何漏洞。
或者手动解析参数并创建包含详细信息的字典:
# parse args
args = parser.parse_args()
args_dict = {}
for group in parser._action_groups:
# split into groups based on title
args_dict[group.title] = {}
for arg in group._group_actions:
if hasattr(args, arg.dest):
args_dict[group.title][arg.dest] = getattr(args, arg.dest)
# or args_dict[arg.dest] = getattr(args, arg.dest)
delattr(args, arg.dest)
# add remaining items into subparser options
args_dict["subparser"] |= vars(args)
return args_dict
要过滤子解析器的参数,可以使用parse_known_args
方法:
car_subparser_args = parser_lan.parse_known_args()[0]
bus_subparser_args = parser_serial.parse_known_args()[0]
print(vars(car_subparser_args)) # -> {'boo': '1', 'foo': '2'}
print(vars(bus_subparser_args)) # -> {'fun': None}