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

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

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

4 ответа

Решение

Вот синтаксический анализатор, который обрабатывает повторный необязательный аргумент 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='*'; 2 строки используются как [-u A [B ...]] или же [-u [A [B ...]]],

Это просто; просто добавьте оба 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']]

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

Добавление с другим в этой теме.

Если вы используете вadd_argument()тогда вы будете получать аргументы в списках внутри списка каждый раз, когда добавляете опцию.

Как вам понравилось:

      [
   ["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"
]
Другие вопросы по тегам