argparse:将参数与另一个参数相关联
使用argparse,可以重复一个参数并将所有值收集到一个列表中:
With argparse, it's possible to repeat an argument and collect all the values into a list:
parser = ArgumentParser()
parser.add_argument('-o', '--output', action='append')
args = parser.parse_args(['-o', 'output1', '-o', 'output2'])
print(vars(args))
# {'output': ['output1', 'output2']}
我正在寻找一种将标记与这些参数相关联的方法,以便可以执行此操作:
I'm looking for a way to associate flags with each of these arguments, so that it's possible to do this:
args = parser.parse_args(['-o', 'output1', '--format', 'text',
'-o', 'output2', '--format', 'csv'])
并获得这样的输出(或类似的结果):
And get an output like this (or something similar):
{'output': {'output1': {'format': 'text'},
'output2': {'format': 'csv'}
}
}
理想情况下,这些标志应遵循通常的语义-例如,-format
可以是可选的,或者每个 -o
输出可能有多个参数,在这种情况下,它们应该以任何顺序通过(即 -o output1 -a -b -c
应当等效于 -o output1 -c -a -b
).
Ideally, these flags should follow the usual semantics - for example, --format
could be optional, or there could be multiple arguments associated with each -o
output, in which case they should be passable in any order (i.e. -o output1 -a -b -c
should be equivalent to -o output1 -c -a -b
).
这可以用argparse完成吗?
Can this be done with argparse?
基于 hpaulj的答案中的代码,我已对实现进行了调整,以使其更加通用.它由2个自定义Action类组成,分别是 ParentAction
和 ChildAction
.
Based on the code in hpaulj's answer, I've tweaked the implementation to make it more versatile. It consists of 2 custom Action classes, ParentAction
and ChildAction
.
parser = argparse.ArgumentParser()
# first, create a parent action:
parent = parser.add_argument('-o', '--output', action=ParentAction)
# child actions require a `parent` argument:
parser.add_argument('-f', '--format', action=ChildAction, parent=parent)
# child actions can be customized like all other Actions. For example,
# we can set a default value or customize its behavior - the `sub_action`
# parameter takes the place of the usual `action` parameter:
parser.add_argument('-l', '--list', action=ChildAction, parent=parent,
sub_action='append', default=[])
args = parser.parse_args(['-o', 'output1', '-l1', '-l2',
'-o', 'output2', '--format', 'csv',
'-o', 'output3', '-f1', '-f2'])
print(args)
# output (formatted):
# Namespace(
# output=OrderedDict([('output1', Namespace(list=['1', '2'])),
# ('output2', Namespace(format='csv', list=[])),
# ('output3', Namespace(format='2', list=[]))
# ])
# )
注意事项
- 子级操作必须始终在父级操作之后执行.例如,
-format csv -o output1
无效,并显示错误消息. -
即使父母不同,也无法注册多个具有相同名称的ChildAction.
Caveats
- Child actions must always follow after parent actions.
For example,
--format csv -o output1
would not work and show an error message. -
It's not possible to register multiple ChildActions with the same name, even with different parents.
示例:
parent1 = parser.add_argument('-o', '--output', action=ParentAction) parser.add_argument('-f', action=ChildAction, parent=parent1) parent2 = parser.add_argument('-i', '--input', action=ParentAction) parser.add_argument('-f', action=ChildAction, parent=parent2) # throws exception: # argparse.ArgumentError: argument -f: conflicting option string: -f
import argparse from collections import OrderedDict class ParentAction(argparse.Action): def __init__(self, *args, **kwargs): super().__init__(*args, default=OrderedDict(), **kwargs) self.children = [] def __call__(self, parser, namespace, values, option_string=None): items = getattr(namespace, self.dest) nspace = type(namespace)() for child in self.children: if child.default is not None: setattr(nspace, child.name, child.default) items[values] = nspace class ChildAction(argparse.Action): def __init__(self, *args, parent, sub_action='store', **kwargs): super().__init__(*args, **kwargs) self.dest, self.name = parent.dest, self.dest self.action = sub_action self._action = None self.parent = parent parent.children.append(self) def get_action(self, parser): if self._action is None: action_cls = parser._registry_get('action', self.action, self.action) self._action = action_cls(self.option_strings, self.name) return self._action def __call__(self, parser, namespace, values, option_string=None): items = getattr(namespace, self.dest) try: last_item = next(reversed(items.values())) except StopIteration: raise argparse.ArgumentError(self, "can't be used before {}".format(self.parent.option_strings[0])) action = self.get_action(parser) action(parser, last_item, values, option_string)
- Child actions must always follow after parent actions.
For example,