2016-03-22 17 views
27

Próbuję napisać skrypt, który akceptuje wiele źródeł wejściowych i robi coś dla każdego z nich. Coś takiegoUżycie tej samej opcji wiele razy w Argparse Pythona

./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ę dość dowiedzieć się, jak to zrobić przy użyciu argparse, wydaje się, że jest tak skonfigurowany, że każda flaga opcja może być użyta tylko raz. Wiem, jak powiązać wiele argumentów z jedną opcją (nargs = '*' lub nargs = '+'), ale to nadal nie pozwoli mi używać wielokrotnie flagi -i. Jak mam to osiągnąć?

Dla jasności, na końcu chciałbym podać listę ciągów znaków. Więc

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

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

+0

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

Odpowiedz

32

Oto parser, który obsługuje powtarzany 2 argumentu opcjonalnego - z nazwami określonymi 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']]) 

robi to nie obsługiwać sprawy 2 or 3 argument (choć pisałem poprawkę jakiś czas temu dla Pythona bug/problem, który będzie obsługiwać taki zakres).

Jak o odrębnej definicji argument z nargs=3 i metavar=('url','name','other')?

Ta krotka metavar może być również używana z nargs='+' i nargs='*'; 2 ciągi są używane jako [-u A [B ...]] lub [-u [A [B ...]]].

+0

Wow, miłe! Uwielbiam, w jaki sposób funkcja pomocy pokazuje, co reprezentują poszczególne komponenty opcji wieloczęściowej. Użyję tego! –

11

-i powinien być skonfigurowany do akceptowania 3 argumenty i użyć akcję 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']]) 

Aby obsłużyć opcjonalną wartość, można spróbować użyć prostego niestandardowego typu. W tym przypadku argumentem pod numerem -i jest pojedynczy łańcuch rozdzielany przecinkami, a liczba podziałów jest ograniczona do 2. Należy przetworzyć wartości, aby upewnić się, że są określone 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ęcej kontroli, zdefiniuj akcję niestandardową. Ten rozszerza wbudowane w _AppendAction (używany przez action='append'), but just does some range checking on the number of arguments given to -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) 
+0

Brilliant! Dziękuję za pomoc. –

+2

To nie * całkiem * rób to, co chcesz; Brakowało mi, że 'inputX_other_var' jest opcjonalne. – chepner

+0

Po prostu wróciłem do komentarza jako takiego, twoja droga wymaga wszystkich varów. Jest jednak we właściwym kierunku! –

Powiązane problemy