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"]]
John Allard
quelle
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 ...]]].

hpaulj
quelle
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']]
Amir
quelle
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)
chepner
quelle
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