Wielokrotne używanie tej samej opcji w argparse Pythona


85

Próbuję napisać skrypt, który akceptuje wiele źródeł wejściowych i robi coś z każdym z nich. Coś takiego

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

Ale nie mogę się do końca dowiedzieć, jak to zrobić za pomocą argparse. Wygląda na to, że jest skonfigurowany tak, że każdej flagi opcji można użyć tylko raz. Wiem, jak powiązać wiele argumentów z jedną opcją ( nargs='*'lub nargs='+'), ale to nadal nie pozwala mi -iwielokrotnie używać flagi. Jak mam się do tego zabrać?

Żeby było jasne, na koniec chciałbym otrzymać listę list łańcuchów. Więc

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

Dlaczego więc nie powiązać wielu argumentów źródła wejściowego z tą jedną opcją?
TigerhawkT3

Ponieważ każde z wielu źródeł wejściowych również musi mieć wiele argumentów w postaci ciągów. Chciałbym użyć flagi -i dla każdego wejścia, a każde wejście zawierałoby wszystkie łańcuchy między kolejnymi flagami -i. Chcę, żeby działał jak ffmpeg, gdzie określasz dane wejściowe za pomocą -i
John Allard

Odpowiedzi:


97

Oto parser, który obsługuje powtórzone 2 argumenty opcjonalne - z nazwami zdefiniowanymi w 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']])

To nie załatwia 2 or 3 argumentsprawy (chociaż napisałem jakiś czas temu łatkę na błąd / problem Pythona, który obsługiwałby taki zakres).

Co powiesz na oddzielną definicję argumentu z nargs=3i metavar=('url','name','other')?

Krotki metavarmożna również używać z nargs='+'i nargs='*'; 2 struny są używane jako [-u A [B ...]]lub [-u [A [B ...]]].


1
O, nieźle! Uwielbiam, jak funkcja pomocy pokazuje, co reprezentują poszczególne komponenty opcji wieloczęściowej. Będę tego używać!
John Allard

49

To jest proste; po prostu dodaj oba action='append'i nargs='*'(lub '+').

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

A kiedy go uruchomisz, otrzymasz

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
Dzięki, dokładnie to, czego potrzebowałem! : D Nota boczna: możliwą wartością domyślną musi być typ list / array, w przeciwnym razie Argparse zawiedzie
Tarwin

22

-ipowinien być skonfigurowany tak, aby akceptował 3 argumenty i używał appendakcji.

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

Aby obsłużyć opcjonalną wartość, możesz spróbować użyć prostego typu niestandardowego. W tym przypadku argument do -ijest pojedynczym łańcuchem rozdzielanym przecinkami, z liczbą podziałów ograniczoną do 2. Konieczne byłoby przetworzenie wartości końcowych, aby upewnić się, że określono co najmniej dwie wartości.

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

Aby uzyskać większą kontrolę, zdefiniuj akcję niestandardową. Ten rozszerza wbudowane _AppendAction(używane przez action='append'), ale po prostu sprawdza zakres liczby argumentów podanych -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
Znakomity! Dziękuję za pomoc.
John Allard

2
Nie dość tego, co chcesz; Brakowało mi, że inputX_other_varjest to opcjonalne.
chepner

Właśnie wróciłem, aby skomentować jako taki, twój sposób wymaga wszystkich vars. Ale to dobry kierunek!
John Allard

1
OK, zaktualizowano kilkoma opcjami obsługi opcjonalnego trzeciego argumentu.
chepner
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.