Verwenden Sie dieselbe Option mehrmals in Pythons Argparse


82

Ich versuche, ein Skript zu schreiben, das mehrere Eingabequellen akzeptiert und mit jeder etwas macht. Etwas wie das

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

Aber ich kann nicht genau herausfinden, wie das geht argparse. Es scheint so eingerichtet zu sein, dass jedes Optionsflag nur einmal verwendet werden kann. Ich weiß, wie man einer einzelnen Option ( nargs='*'oder nargs='+') mehrere Argumente zuordnet , aber das lässt mich das -iFlag trotzdem nicht mehrmals verwenden. Wie mache ich das?

Um ganz klar zu sein, möchte ich am Ende eine Liste von Zeichenfolgenlisten. Damit

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

Warum also nicht die Argumente für mehrere Eingabequellen dieser einzelnen Option zuordnen?
TigerhawkT3

Weil jede der mehreren Eingabequellen auch mehrere Zeichenfolgenargumente haben muss. Ich möchte das Flag -i für jede der Eingaben verwenden müssen, und jede Eingabe würde alle Zeichenfolgen zwischen aufeinanderfolgenden -i-Flags enthalten. Ich möchte, dass es wie ffmpeg funktioniert, wo Sie Eingaben mit -i
John Allard

Antworten:


96

Hier ist ein Parser, der ein wiederholtes 2 optionales Argument optional behandelt - mit Namen, die definiert sind in 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']])

Dies behandelt den 2 or 3 argumentFall nicht (obwohl ich vor einiger Zeit einen Patch für einen Python-Fehler geschrieben habe, der einen solchen Bereich behandeln würde).

Wie wäre es mit einer separaten Argumentdefinition mit nargs=3und metavar=('url','name','other')?

Das Tupel metavarkann auch mit nargs='+'und verwendet werden nargs='*'; Die 2 Zeichenfolgen werden als [-u A [B ...]]oder verwendet [-u [A [B ...]]].


1
Wow nett! Ich finde es toll, wie die Hilfefunktion zeigt, was die einzelnen Komponenten der mehrteiligen Option darstellen. Ich werde das benutzen!
John Allard

48

Das ist einfach; füge einfach beide action='append'und nargs='*'(oder '+') hinzu.

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

Wenn Sie es dann ausführen, erhalten Sie

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
Danke, genau das, was ich brauchte! : D Randnotiz: Ein möglicher Standard muss Typ Liste / Array sein, sonst schlägt Argparse fehl
Tarwin

22

-isollte so konfiguriert sein, dass 3 Argumente akzeptiert und die appendAktion verwendet werden.

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

Um einen optionalen Wert zu verarbeiten, können Sie versuchen, einen einfachen benutzerdefinierten Typ zu verwenden. In diesem Fall ist das Argument to -ieine einzelne durch Kommas getrennte Zeichenfolge, wobei die Anzahl der Teilungen auf 2 begrenzt ist. Sie müssten die Werte nachbearbeiten, um sicherzustellen, dass mindestens zwei Werte angegeben sind.

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

Definieren Sie für mehr Kontrolle eine benutzerdefinierte Aktion. Dieser erweitert die integrierte _AppendAction(von action='append') verwendete Funktion, überprüft jedoch nur den Bereich, in dem die Anzahl der Argumente angegeben ist -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
Brillant! Danke für deine Hilfe.
John Allard

2
Dies macht nicht ganz das, was Sie wollen; Ich habe vermisst, dass inputX_other_vardas optional ist.
chepner

Ich bin gerade zurückgekommen, um als solche zu kommentieren, dein Weg erfordert alle Vars. Es ist jedoch in die richtige Richtung!
John Allard

1
OK, aktualisiert mit einigen Optionen für die Behandlung eines optionalen dritten Arguments.
Chepner
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.