Використовуючи один і той же параметр кілька разів у argparse Python


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='*'; 2 рядки використовуються як [-u A [B ...]]або [-u [A [B ...]]].


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 Примітка збоку: можливим за замовчуванням має бути список типів / масив, інакше 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']])

Для обробки необов’язкового значення можна спробувати скористатися простим спеціальним типом. У цьому випадку аргументом -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необов’язково.
Чепнер

Я щойно повернувся, щоб прокоментувати як такий, твій шлях вимагає всіх варіацій. Це все у правильному напрямку!
Джон Аллард,

1
Добре, оновлено кількома варіантами обробки необов’язкового 3-го аргументу.
Чепнер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.