在Python的argparse中多次使用同一选项

144

我正在尝试编写一个脚本,接受多个输入源并对每个源执行某些操作。类似于这样:

./my_script.py \
    -i input1_url input1_name input1_other_var \
    -i input2_url input2_name input2_other_var \
    -i input3_url input3_name
# notice inputX_other_var is optional

但是我不太清楚如何使用argparse来实现这一点。它似乎被设置为每个选项标志只能使用一次。我知道如何将多个参数与单个选项关联(nargs ='*'nargs ='+'),但那仍然不能让我多次使用-i标志。我该如何完成这个任务?

仅仅为了明确,最终我想要的是一个字符串列表的列表。

[["input1_url", "input1_name", "input1_other"],
 ["input2_url", "input2_name", "input2_other"],
 ["input3_url", "input3_name"]]

1
那么为什么不将多个输入源参数与单个选项关联起来呢? - TigerhawkT3
因为每个多重输入源也需要有多个字符串参数。我想要使用-i标志来处理每一个输入,每个输入都包含在连续的-i标志之间的所有字符串。我希望它能像ffmpeg一样工作,你可以用-i指定输入。 - John Allard
4个回答

131

这是一个处理重复的具有两个可选参数的解析器 - 这些参数是在 metavar 中定义的名称:

parser=argparse.ArgumentParser()
parser.add_argument('-i','--input',action='append',nargs=2,
    metavar=('url','name'),help='help:')

In [295]: parser.print_help()
usage: ipython2.7 [-h] [-i url name]

optional arguments:
  -h, --help            show this help message and exit
  -i url name, --input url name
                        help:

In [296]: parser.parse_args('-i one two -i three four'.split())
Out[296]: Namespace(input=[['one', 'two'], ['three', 'four']])

这不处理2或3个参数的情况(尽管我曾经为Python的一个错误/问题编写过一个补丁,可以处理这样的范围)。

那么使用一个单独的参数定义,包括nargs=3metavar=('url','name','other')如何?

元组metavar也可以与nargs='+'nargs='*'一起使用;这两个字符串用作[-u A [B ...]][-u [A [B ...]]]


115

这很简单;只需要同时添加action='append'nargs='*'(或者'+')即可。

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-i', action='append', nargs='+')
args = parser.parse_args()

然后当你运行它时,你会得到:

In [32]: run test.py -i input1_url input1_name input1_other_var -i input2_url i
...: nput2_name input2_other_var -i input3_url input3_name

In [33]: args.i
Out[33]:
[['input1_url', 'input1_name', 'input1_other_var'],
 ['input2_url', 'input2_name', 'input2_other_var'],
 ['input3_url', 'input3_name']]

9
谢谢,这正是我需要的!:D另外说明一下,可能需要将默认值设为列表/数组类型,否则Argparse会失败。 - Tarwin
请注意,您可以为 -i 放置超过三个参数:您应该检查并在用户添加超过3个参数时引发错误。 - drevicko

33

-i 应该被配置为接受3个参数并使用 append 操作。

>>> p = argparse.ArgumentParser()
>>> p.add_argument("-i", nargs=3, action='append')
_AppendAction(...)
>>> p.parse_args("-i a b c -i d e f -i g h i".split())
Namespace(i=[['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']])

为了处理可选值,您可以尝试使用简单的自定义类型。在这种情况下,-i的参数是一个由逗号分隔的字符串,最多分割两次。您需要对值进行后处理,以确保至少指定了两个值。

>>> p.add_argument("-i", type=lambda x: x.split(",", 2), action='append')
>>> print p.parse_args("-i a,b,c -i d,e -i g,h,i,j".split())
Namespace(i=[['a', 'b', 'c'], ['d', 'e'], ['g', 'h', 'i,j']])
为了更加灵活控制,可以定义一个自定义操作。这个操作继承了内置的_AppendAction(由action ='append'使用),但只是对给定的-i参数数量进行一些范围检查。

为了更好地控制,请定义自定义操作。此示例扩展了内置操作 _AppendAction (由 action ='append'使用),但只是对给定参数 -i 的数量进行了一些范围检查。

class TwoOrThree(argparse._AppendAction):
    def __call__(self, parser, namespace, values, option_string=None):
        if not (2 <= len(values) <= 3):
            raise argparse.ArgumentError(self, "%s takes 2 or 3 values, %d given" % (option_string, len(values)))
        super(TwoOrThree, self).__call__(parser, namespace, values, option_string)

p.add_argument("-i", nargs='+', action=TwoOrThree)

谢谢你的提示!我有不同的问题,因为附加操作是附加到给定默认值而不是替换它,并且我需要一个平面列表。因此,我使用了抽象基类“Action”。 - TrueY

13

在此线程中添加“Other”。

如果您在add_argument()中使用action='append',则每次添加选项时将以列表的形式获得参数列表。

就像你喜欢的那样:

[
   ["input1_url", "input1_name", "input1_other"],
   ["input2_url", "input2_name", "input2_other"],
   ["input3_url", "input3_name"]
]

但如果有人想在同一个list[]中使用这些参数,那么可以在你的代码中使用action='extend'而不是action='append'。这将使你得到一个包含所有参数的单个列表。

[
  "input1_url", 
  "input1_name", 
  "input1_other", 
  "input2_url", 
  "input2_name", 
  "input2_other", 
  "input3_url", 
  "input3_name"
]

2
你的答案在底部,但我正需要这个“extend”部分 :) 谢谢! - Rafaó
1
是的,确切就是那个答案。 - Demetry Pascal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接