这是一个我整理的简单技巧,可能是一个不错的起点:
import argparse
class PositionalAction(argparse.Action):
def __call__(self,parser,namespace,values,option_string=None):
lst = getattr(namespace,self.dest)
lst.append(values)
parser.last_positional_values = lst
all_positional = getattr(namespace,'all_positional',[])
all_positional.append(lst)
namespace.all_positional = all_positional
class AssociateAction(argparse.Action):
def __call__(self,parser,namespace,values,option_string=None):
try:
parser.last_positional_values.append(values)
except AttributeError:
pass
parser = argparse.ArgumentParser()
parser.add_argument('-o',action=AssociateAction,dest=argparse.SUPPRESS)
junk,unknown = parser.parse_known_args()
for i,_ in enumerate(unknown):
parser.add_argument('arg%d'%i,action=PositionalAction,default=[])
print parser.parse_args()
这是它的实际运用:
temp $ python test1.py foo -o 1 bar -o 2 baz qux -o 4
Namespace(all_positional=[['foo', '1'], ['bar', '2'], ['baz'], ['qux', '4']], arg0=['foo', '1'], arg1=['bar', '2'], arg2=['baz'], arg3=['qux', '4'])
这个问题有几个挑战。首先,你想接受任意数量的位置参数 - argparse 不喜欢那样做。argparse 想要事先知道要期望什么。解决方案是构建一个解析器并解析命令行,但告诉 argparse 仅解析已知参数(在这种情况下,非位置
-o
参数都被静默解析,但“位置”参数没有被解析)。
parse_known_args
对此非常完美,因为它返回一个元组,格式为
(已解析内容的名称空间,未知参数)
。现在我们知道了未知的参数 - 我们只需要为每个参数向解析器添加一个位置参数,以使 parse_args 正常运行。
那么,自定义操作实际上是在做什么呢?当发现一个位置参数(第二次遍历时),我们获得默认值(即列表)并将该值添加到该列表中(此后我将其称为“值”列表)。然后,我们通过引用“值”列表来修改解析器。我们还从名称空间获取“all_positional”列表。如果它没有该属性,我们只会得到一个空列表。我们将“值”列表添加到“all_positional”列表中,并将其放回名称空间中。
现在,当我们遇到一个
-o
标志时,我们查看解析器以获取“值”列表,并将附加值添加到该列表中。我们可以完全不接触解析器做同样的事情...(我们可以查看
namespace.all_positional[-1]
- 它是与
parser.last_positional_values
相同的列表)。