Использование одной и той же опции несколько раз в argparse Python

Я пытаюсь написать сценарий, который принимает несколько источников ввода и что-то делает с каждым из них. Что-то вроде этого!--7-->

./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"]]

3 ответов


вот синтаксический анализатор, который обрабатывает повторный аргумент 2 необязательно - с именами, определенными в 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 or 3 argument case (хотя я написал патч некоторое время назад для ошибки/проблемы Python, которая будет обрабатывать такой диапазон).

как насчет отдельного определения аргумента с nargs=3 и metavar=('url','name','other')?

кортеж metavar также можно использовать с nargs='+' и nargs='*'; 2 строки используются как [-u A [B ...]] или [-u [A [B ...]]].


-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 - Это одна строка с разделителями-запятыми, количество разбиений ограничено 2. Вы должны после обработки, чтобы убедиться, есть как минимум два значения.

>>> 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.

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='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']]