我会试试你最新的自定义操作:
In [34]: parser=argparse.ArgumentParser()
In [35]: parser.add_argument('--env')
In [36]: parser.add_argument('--props',nargs='*',action=NameValueAction)
Out[36]: NameValueAction(option_strings=['--props'], dest='props', nargs='*', const=None, default=None, type=None, choices=None, help=None, metavar=None)
parse_args 出现 unrecognized arguments 错误。您的操作正确定义为未知:
In [37]: args=parser.parse_args('--env=target_env --props name1=value1 name2=value2 module/'.split())
usage: ipython2.7 [-h] [--env ENV] [--props [PROPS [PROPS ...]]]
ipython2.7: error: unrecognized arguments: module/
...
使用 parse_known_args 我可以看到 args 和 extras 而没有错误消息:
In [38]: parser.parse_known_args('--env=target_env --props name1=value1 name2=value2 module/'.split())
Out[38]:
(Namespace(env='target_env', name1='value1', name2='value2', props=None),
['module/'])
所以--props 之后的所有字符串都作为values 传递给该Action。它将值分配给命名空间,然后返回。 parse_known_args 将 unrecognized 值从命名空间中取出,并将它们放入 extras 列表中。
现在我将添加一个位置,希望它会采用module/ 字符串:
In [39]: parser.add_argument('foo')
In [40]: parser.parse_known_args('--env=target_env --props name1=value1 name2=value2 module/'.split())
usage: ipython2.7 [-h] [--env ENV] [--props [PROPS [PROPS ...]]] foo
ipython2.7: error: too few arguments
...
糟糕,一个不同的错误,即使是parse_known_args。问题是'module/'仍然被分配给--props,没有给foo留下任何东西。 --props 有一个 * nargs,这意味着它获取所有符合条件的参数(没有 -)。将“module/”作为unknown 放在命名空间中并没有帮助。解析器不会重新评估此列表中的字符串。
我可以使用“--”来表示后面的所有字符串都是位置字符串。现在--props 不接收或处理“模块”。而是由foo 在下次处理位置时使用。
In [41]: parser.parse_known_args('--env=target_env --props name1=value1 name2=value2 -- module/'.split())
Out[41]:
(Namespace(env='target_env', foo='module/', name1='value1', name2='value2', props=None),
[])
另一个可选参数,例如“--env”可用于标记“--props”参数的结束:
In [42]: parser.parse_known_args('--props name1=value1 name2=value2 --env=target_env module/'.split())
Out[42]:
(Namespace(env='target_env', foo='module/', name1='value1', name2='value2', props=None),
[])
注意progs=None 出现在命名空间中。这是因为解析器在解析开始时会将所有 Action 默认值加载到命名空间中。您可以使用default=argparse.SUPPRESS 来防止这种情况发生。
请参阅此错误/问题以了解有关如何将参数分配给“*”可选的说明,以及如何为以下位置保留一些参数:
http://bugs.python.org/issue9338
argparse optionals with nargs='?', '*' or '+' can't be followed by positionals
https://stackoverflow.com/a/33405960/901925 是另一个最近的 SO 问题,涉及一个常规位置,后跟两个“?”定位。
正如我在评论中指出的,argparse 与 optparse 不同。我相信optparse 每个动作(或等效的)都会消耗尽可能多的字符串,并将其余字符串留给后续动作。在argparse 中,单个操作无权访问主列表 (arg_strings)。解析器决定一个 Action 获得多少个字符串。
更多细节来自argparse.py 文件。是对parse_args相关部分的总结。
_parse_known_args(self, arg_strings, namespace):
# arg_strings - master list of strings from sys.argv
start_index = 0
while start_index<amax:
# step through arg_strings processing postionals and optionals
consume_positionals()
start_index = next_option_string_index
start_index = consume_optional(start_index)
consume_optional(start_index): # function local to _parse_known_args
...
start = start_index + 1
arg_count = <fn of available arguments and nargs>
stop = start + arg_count
args = arg_strings[start:stop]
<action = CustomAction.__call__>
take_action(action, args, option_string)
return stop
take_action(action, argument_strings, ...): # another local function
# argument_strings is a slice of arg_strings
argument_values = self._get_values(action, argument_strings)
# _get_values passes strings through the action.type function
action(self, namespace, argument_values, option_string)
# no return
最终效果是您的CustomAction.__call__ 获得了一个values 列表,这些列表派生自主arg_strings 列表的一部分。它无权访问arg_strings,也无权访问该切片的start 和stop。所以它不能改变分配给它自己或任何后续动作的字符串。
另一个想法是将无法解析的值放入self.dest。
class NameValueAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
extras = []
for value in values:
try:
n, v = value.split('=')
setattr(namespace, n, v)
except ValueError:
extras.append(value)
if len(extras):
setattr(namespace, self.dest, extras)
然后解析(没有foo 位置)会产生:
In [56]: parser.parse_args('--props name1=value1 p1 name2=value2 module/'.split())
Out[56]: Namespace(env=None, name1='value1', name2='value2', props=['p1', 'module/'])
args.props 现在包含 ['p1','module/'],--props 得到的字符串,但无法解析为 n=v 对。这些可以根据需要在解析后重新分配。