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

85

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

./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"]]
Джон Аллард
источник
Так почему бы не связать несколько аргументов источника ввода с одним параметром?
TigerhawkT3,
Поскольку каждый из нескольких источников ввода также должен иметь несколько строковых аргументов. Я хотел бы использовать флаг -i для каждого из входов, и каждый вход будет содержать все строки между последовательными флагами -i. Я хочу, чтобы он работал как ffmpeg, где вы указываете входные данные с помощью -i
Джон Аллард,

Ответы:

97

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

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

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

hpaulj
источник
1
Вау круто! Мне нравится, как функция справки показывает, что представляют собой отдельные компоненты опции, состоящей из нескольких частей. Я воспользуюсь этим!
Джон Аллард,
49

Это просто; просто добавьте оба 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']]
Амир
источник
2
Спасибо, именно то, что мне нужно! : D Боковое примечание: возможное значение по умолчанию должно быть типом list / array, иначе Argparse не
сработает
22

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

Для обработки необязательного значения вы можете попробовать использовать простой настраиваемый тип. В этом случае аргумент to -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)
Чепнер
источник
1
Гениально! Спасибо за помощь.
Джон Аллард,
2
Это не совсем то, что вам нужно; Я пропустил это inputX_other_varнеобязательно.
chepner
Я только что вернулся, чтобы прокомментировать, ваш способ требует всех варов. Но это в правильном направлении!
Джон Аллард,
1
Хорошо, обновлено парой опций для обработки необязательного третьего аргумента.
chepner