在重复组上使用python argparse

时间:2014-12-28 23:31:31

标签: python argparse

我正在编写一个带有一个必需参数的脚本,然后可以根据它更改以下参数的解释。大多数组合进展顺利,但有一个给我带来麻烦。它是一个由三个参数组成的重复组,所有字符串。例如:

$ python script.py p1 p2_1 p2_2 p2_3 p3_1 p3_2 p3_3

或伪正则表达式:

$ python script.py p1 (px_1 px_2 px_3)+

我无法控制输入的格式。可以选择通过stdin接收此命令而不是命令行。使用正则表达式将字符串作为字符串处理可能更容易,这也允许我通过加入argv来处理它们。

还有其他一些SO答案可以解决与argparse类似的问题。

hpaulj在这里有两个有用的回复:Argparse, handle repeatable set of items 在这里:Python argparser repeat subparse

现在几个小时后,我还没弄明白如何在没有hackery的情况下使用argparse来完成这项工作。首先,剥离第一个参数,然后迭代直到剩下的参数消失。我想将它保存在同一个命名空间对象中,就像我可以弄清楚如何正确地执行此操作一样。一些演示代码基于以上答案之一:

#!/usr/bin/env python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('param1', type=str)
param1, remaining = parser.parse_known_args()

parser = argparse.ArgumentParser()
parser.add_argument('first', type=str, action='append')
parser.add_argument('second', type=str, action='append')
parser.add_argument('third', type=str, action='append')

repeating_args, remaining = parser.parse_known_args(remaining)
while remaining:
    another_set, remaining = parser.parse_known_args(remaining)
    repeating_args.first.append(another_set.first[0])
    repeating_args.second.append(another_set.second[0])
    repeating_args.third.append(another_set.third[0])

但这对我来说感觉很糟糕,它迫使我以影响其他参数组合的方式修改代码。使用argparse有更好的方法吗?或者,如果我对此不满意,我应该不使用argparse吗?不幸的是,这意味着我要重写我的很多代码......

感谢。

更新后的代码:

根据hpaulj的答案,这是我正在使用的代码的压缩和工作版本。它比上面的代码要好得多,因为它在解析器配置方面相对通用。我希望它有所帮助。

#!/usr/bin/env python
import sys
import argparse

def parse_args():

    # Create the first parser object and get just the first parameter
    parser = argparse.ArgumentParser('Argument format parser')
    parser.add_argument('arg_format', type=str, help='The first argument.' +
                        'It tells us what input to expect next.')
    args_ns, remaining = parser.parse_known_args()

    # Generate a new parser based on the first parameter
    parser = formatSpecificParser(args_ns.arg_format)

    # There will always be at least one set of input (in this case at least)
    args_ns, remaining = parser.parse_known_args(args=remaining, namespace=args_ns)

    # Iterate over the remaining input, if any, adding to the namespace
    while remaining:
        args_ns, remaining = parser.parse_known_args(args=remaining,
                                                     namespace=args_ns)

    return args_ns

def formatSpecificParser(arg_format):
    parser = argparse.ArgumentParser("Command line parser for %s" % arg_format)
    if (arg_format == "format_1"):
        addArgsFormat1(parser)
    # elif (...):
        # other format function calls
    return parser

def addArgsFormat1(parser):
    parser.add_argument('arg1', type=str, action='append', help='helpful text')
    parser.add_argument('arg2', type=str, action='append', help='helpful text')
    parser.add_argument('arg3', type=str, action='append', help='helpful text')

def main(argv):
    args = parse_args()
    print (args)

if __name__ == "__main__":
    main(sys.argv[1:])

命令行输出:

$ ./example.py format_1 foo bar baz meh meh meh e pluribus unum
Namespace(arg1=['foo', 'meh', 'e'], arg2=['bar', 'meh', 'pluribus'], arg3=['baz', 'meh', 'unum'], arg_format='format_1')

1 个答案:

答案 0 :(得分:0)

这是一个粗略的序列,可以简化重复的部分:

In [10]: p=argparse.ArgumentParser()

In [11]: p.add_argument('p2',nargs=3,action='append')

In [12]: ns,rest=p.parse_known_args('p21 p22 p23 p31 p32 p33'.split())

In [13]: ns
Out[13]: Namespace(p2=[['p21', 'p22', 'p23']])

In [14]: rest
Out[14]: ['p31', 'p32', 'p33']

In [15]: ns,rest=p.parse_known_args(rest,ns)  # repeat as needed

In [16]: ns
Out[16]: Namespace(p2=[['p21', 'p22', 'p23'], ['p31', 'p32', 'p33']])

正常情况下,'追加'对位置有意义,因为它们不能重复。但是在这里它可以方便地生成一个子列表列表。将较早的命名空间传递到下一个解析步骤可以构建已经解析过的值。这应该与你的3个位置参数同样适用nargs=3