Wie kann ich eine Liste als Befehlszeilenargument mit argparse übergeben?

441

Ich versuche, eine Liste als Argument an ein Befehlszeilenprogramm zu übergeben. Gibt es eine argparseOption, um eine Liste als Option zu übergeben?

parser.add_argument('-l', '--list',
                      type=list, action='store',
                      dest='list',
                      help='<Required> Set flag',
                      required=True)

Das Skript wird wie folgt aufgerufen

python test.py -l "265340 268738 270774 270817"
user2125827
quelle

Antworten:

878

TL; DR

Verwenden Sie die nargsOption oder die 'append'Einstellung der actionOption (je nachdem, wie sich die Benutzeroberfläche verhalten soll).

Nargs

parser.add_argument('-l','--list', nargs='+', help='<Required> Set flag', required=True)
# Use like:
# python arg.py -l 1234 2345 3456 4567

nargs='+'nimmt 1 oder mehr Argumente, nargs='*'nimmt null oder mehr.

anhängen

parser.add_argument('-l','--list', action='append', help='<Required> Set flag', required=True)
# Use like:
# python arg.py -l 1234 -l 2345 -l 3456 -l 4567

Mit appendbieten Sie die Möglichkeit, die Liste mehrmals aufzubauen.

Nicht benutzen type=list!!! - Es gibt wahrscheinlich keine Situation, in der Sie verwenden möchtentype=list mit argparse. Je.


Schauen wir uns einige der verschiedenen Möglichkeiten und das Endergebnis genauer an.

import argparse

parser = argparse.ArgumentParser()

# By default it will fail with multiple arguments.
parser.add_argument('--default')

# Telling the type to be a list will also fail for multiple arguments,
# but give incorrect results for a single argument.
parser.add_argument('--list-type', type=list)

# This will allow you to provide multiple arguments, but you will get
# a list of lists which is not desired.
parser.add_argument('--list-type-nargs', type=list, nargs='+')

# This is the correct way to handle accepting multiple arguments.
# '+' == 1 or more.
# '*' == 0 or more.
# '?' == 0 or 1.
# An int is an explicit number of arguments to accept.
parser.add_argument('--nargs', nargs='+')

# To make the input integers
parser.add_argument('--nargs-int-type', nargs='+', type=int)

# An alternate way to accept multiple inputs, but you must
# provide the flag once per input. Of course, you can use
# type=int here if you want.
parser.add_argument('--append-action', action='append')

# To show the results of the given option to screen.
for _, value in parser.parse_args()._get_kwargs():
    if value is not None:
        print(value)

Hier ist die Ausgabe, die Sie erwarten können:

$ python arg.py --default 1234 2345 3456 4567
...
arg.py: error: unrecognized arguments: 2345 3456 4567

$ python arg.py --list-type 1234 2345 3456 4567
...
arg.py: error: unrecognized arguments: 2345 3456 4567

$ # Quotes won't help here... 
$ python arg.py --list-type "1234 2345 3456 4567"
['1', '2', '3', '4', ' ', '2', '3', '4', '5', ' ', '3', '4', '5', '6', ' ', '4', '5', '6', '7']

$ python arg.py --list-type-nargs 1234 2345 3456 4567
[['1', '2', '3', '4'], ['2', '3', '4', '5'], ['3', '4', '5', '6'], ['4', '5', '6', '7']]

$ python arg.py --nargs 1234 2345 3456 4567
['1234', '2345', '3456', '4567']

$ python arg.py --nargs-int-type 1234 2345 3456 4567
[1234, 2345, 3456, 4567]

$ # Negative numbers are handled perfectly fine out of the box.
$ python arg.py --nargs-int-type -1234 2345 -3456 4567
[-1234, 2345, -3456, 4567]

$ python arg.py --append-action 1234 --append-action 2345 --append-action 3456 --append-action 4567
['1234', '2345', '3456', '4567']

Imbissbuden :

  • Verwenden nargs oderaction='append'
    • nargs kann aus Anwendersicht einfacher sein, aber es kann nicht intuitiv sein, wenn es Positionsargumente gibt, weil argparse nicht gesagt werden kann, was ein Positionsargument sein soll und was zu dem gehört nargs; Wenn Sie dann Positionsargumente habenaction='append' möglicherweise die bessere Wahl.
    • Das Obige ist nur wahr, wenn nargsgegeben '*'ist '+', oder '?'. Wenn Sie eine Ganzzahl (z. B. 4) angeben, gibt es keine Probleme beim Mischen von Optionen mit nargsund Positionsargumenten, daargparse genau bekannt ist, wie viele Werte für die Option zu erwarten sind.
  • Verwenden Sie keine Anführungszeichen in der Befehlszeile 1
  • Nicht benutzen type=list , da eine Liste mit Listen zurückgegeben wird
    • Dies geschieht, weil unter der Haube argparseder Wert von verwendet wird type, um jedes einzelne gegebene Argument zu erzwingen Ihnen gewähltetype , nicht das Aggregat aller Argumente.
    • Sie können type=int(oder was auch immer) verwenden, um eine Liste von Ints (oder was auch immer) zu erhalten.

1 : Ich meine im Allgemeinen nicht. Ich meine, Anführungszeichen zu verwenden, um eine Liste zu übergeben,argparse ist nicht das, was Sie wollen.

SethMMorton
quelle
3
Was ist mit einer Liste von Zeichenfolgen? Dadurch werden mehrere Zeichenfolgenargumente ("wassup", "etwas" und "else") in eine Liste von Listen umgewandelt, die folgendermaßen aussehen: [['w', 'a', 's', 's', 'u' , 'p'], ['s', 'o', 'm', 'e', ​​'t', 'h', 'i', 'n', 'g'], ['e', ' l ',' s ',' e ']]
rd108
3
@ rd108 Ich verstehe, ich wette, dass Sie die type=listOption verwenden. Benutze das nicht. Das macht aus einer Zeichenfolge eine Liste und damit die Listen der Listen.
SethMMorton
1
@Dror Alle Eingaben werden als Zeichenfolgen angenommen, es sei denn, Sie setzen den typeParameter auf ein anderes Objekt. Standardmäßig gibt diese Methode eine Liste von Zeichenfolgen zurück.
SethMMorton
1
--könnte Optionen gegen Positionsargumente aufteilen. prog --opt1 par1 ... -- posp1 posp2 ...
0andriy
1
Es kann nicht intuitiv sein, wenn es Positionsargumente gibt, da argparse nicht sagen kann, was ein Positionsargument sein soll und was zu den Nargs gehört . --hilft, dies herauszufinden, wie im Beispiel in meinem vorherigen Kommentar gezeigt. IOW-Benutzer liefert --gefolgt von allen Positionsargumenten.
0andriy
83

Ich bevorzuge es, eine begrenzte Zeichenfolge zu übergeben, die ich später im Skript analysiere. Die Gründe dafür sind: die Liste kann von jeder Art sein , intoder str, und manchmal mit nargsmir auf Probleme stoßen , wenn es mehr optionalen Argumente und Positionsargumente sind.

parser = ArgumentParser()
parser.add_argument('-l', '--list', help='delimited list input', type=str)
args = parser.parse_args()
my_list = [int(item) for item in args.list.split(',')]

Dann,

python test.py -l "265340,268738,270774,270817" [other arguments]

oder,

python test.py -l 265340,268738,270774,270817 [other arguments]

wird gut funktionieren. Das Trennzeichen kann auch ein Leerzeichen sein, das jedoch Anführungszeichen um den Argumentwert erzwingt, wie im Beispiel in der Frage.

Dojuba
quelle
57
Sie können das typeArgument auf lambda s: [int(time) for item in s.split(',')]anstatt auf Nachbearbeitung setzen args.list.
Chepner
13
@ chepner, ja du hast absolut recht und es wäre pythonischer - nur ein kleiner Tippfehler: int(time)sollte sein int(item). Mein Beispiel war eine vereinfachte Version von dem, was ich normalerweise mache, wo ich viele andere Dinge überprüfe, anstatt eine einfache Verarbeitung. Aber um die Frage einfach zu beantworten, finde ich auch Ihren Weg eleganter ..
Dojuba
1
Diese Antwort scheint die pythonischste zu sein
Quetzalcoatl
1
Der Kommentar von @chepner ist einige ernsthafte Ninja Skillz +1
Briford Wylie
1
lambda items: list(csv.reader([items]))[0]Mit der Standard- CSV- Bibliothek ist eine modifizierte Version des Kommentars von @chepner für alle, die sich Sorgen über willkürliche CSV-Eingaben machen ( siehe Antwort von @adamk ).
Kevin
19

Zusätzlich nargsmöchten Sie möglicherweise Folgendes verwenden, choiceswenn Sie die Liste im Voraus kennen:

>>> parser = argparse.ArgumentParser(prog='game.py')
>>> parser.add_argument('move', choices=['rock', 'paper', 'scissors'])
>>> parser.parse_args(['rock'])
Namespace(move='rock')
>>> parser.parse_args(['fire'])
usage: game.py [-h] {rock,paper,scissors}
game.py: error: argument move: invalid choice: 'fire' (choose from 'rock',
'paper', 'scissors')
Martin Thoma
quelle
10

Verwenden des nargs-Parameters nargs in der Methode add_argument von argparse

Ich benutze nargs = ' ' als add_argument Parameter. Ich habe speziell nargs = 'verwendet ' für die Option auszuwählen, wenn ich keine expliziten Argumente übergebe

Einschließlich eines Code-Snippets als Beispiel:

Beispiel: temp_args1.py

Bitte beachten Sie: Der folgende Beispielcode ist in Python3 geschrieben. Durch Ändern des Druckanweisungsformats kann in python2 ausgeführt werden

#!/usr/local/bin/python3.6

from argparse import ArgumentParser

description = 'testing for passing multiple arguments and to get list of args'
parser = ArgumentParser(description=description)
parser.add_argument('-i', '--item', action='store', dest='alist',
                    type=str, nargs='*', default=['item1', 'item2', 'item3'],
                    help="Examples: -i item1 item2, -i item3")
opts = parser.parse_args()

print("List of items: {}".format(opts.alist))

Hinweis: Ich sammle mehrere Zeichenfolgenargumente, die in der Liste gespeichert werden - opts.alist Wenn Sie eine Liste von Ganzzahlen wünschen, ändern Sie den Typparameter in parser.add_argument in int

Ausführungsergebnis:

python3.6 temp_agrs1.py -i item5 item6 item7
List of items: ['item5', 'item6', 'item7']

python3.6 temp_agrs1.py -i item10
List of items: ['item10']

python3.6 temp_agrs1.py
List of items: ['item1', 'item2', 'item3']
Py_minion
quelle
1
@Py_minion Gibt es eine Möglichkeit, eine Liste als Argument zu verwenden und die Ausgabe auch als Liste zu haben? temp_args1.py -i [item5 ,item6, item7]und lassen Sie die Ausgabe auch als Liste herauskommen (anstelle der verschachtelten Liste)
Moondra
@ Moondra Ja. Ich bin froh, dass du gefragt hast. `` `parser.add_argument ('- o', '--options', action = 'store', dest = 'opt_list', type = str, nargs = '*', default = sample_list, help =" String of database Beispiele: \ -o Option1 Option2, -o Option3 ")` `` Hier ist 'sample_list' vom Typ Liste mit Standardoptionen. Beispiel: sample_list = [Option4, Option5]
Py_minion
1
@ Py_minion Danke. Ich werde es später heute testen.
Moondra
Ich habe dies verwendet, dies ist sehr nützlich, um das Erstellen von Listen aus den Argumenten zu übergeben.
Geschwister
5

Wenn Sie beabsichtigen, einen einzelnen Schalter für mehrere Parameter zu verwenden, verwenden Sie nargs='+'. Wenn Ihr Beispiel '-l' tatsächlich ganze Zahlen nimmt:

a = argparse.ArgumentParser()
a.add_argument(
    '-l', '--list',  # either of this switches
    nargs='+',       # one or more parameters to this switch
    type=int,        # /parameters/ are ints
    dest='list',     # store in 'list'.
    default=[],      # since we're not specifying required.
)

print a.parse_args("-l 123 234 345 456".split(' '))
print a.parse_args("-l 123 -l=234 -l345 --list 456".split(' '))

Produziert

Namespace(list=[123, 234, 345, 456])
Namespace(list=[456])  # Attention!

Wenn Sie dasselbe Argument mehrmals angeben, wird die Standardaktion ('store' ) die vorhandenen Daten.

Die Alternative besteht darin, die folgende appendAktion zu verwenden:

a = argparse.ArgumentParser()
a.add_argument(
    '-l', '--list',  # either of this switches
    type=int,        # /parameters/ are ints
    dest='list',     # store in 'list'.
    default=[],      # since we're not specifying required.
    action='append', # add to the list instead of replacing it
)

print a.parse_args("-l 123 -l=234 -l345 --list 456".split(' '))

Welches produziert

Namespace(list=[123, 234, 345, 456])

Oder Sie können einen benutzerdefinierten Handler / eine benutzerdefinierte Aktion schreiben, um durch Kommas getrennte Werte zu analysieren, damit Sie dies tun können

-l 123,234,345 -l 456
kfsone
quelle
5

In add_argument(), typeist nur ein aufrufbare Objekt , das String und gibt Optionswert erhält.

import ast

def arg_as_list(s):                                                            
    v = ast.literal_eval(s)                                                    
    if type(v) is not list:                                                    
        raise argparse.ArgumentTypeError("Argument \"%s\" is not a list" % (s))
    return v                                                                   


def foo():
    parser.add_argument("--list", type=arg_as_list, default=[],
                        help="List of values")

Dies ermöglicht:

$ ./tool --list "[1,2,3,4]"
Wonder.mice
quelle
Beachten Sie, dass für diese Methode, wenn Zeichenfolgen übergeben werden müssen, diese in der Befehlszeile entsprechend angegeben werden müssen. Ein Benutzer kann dies unerwartet finden. Wenn nur ganze Zahlen analysiert werden, ist dies in Ordnung.
SethMMorton
0

Wenn Sie eine verschachtelte Liste haben, in der die inneren Listen unterschiedliche Typen und Längen haben, und Sie den Typ beibehalten möchten, z.

[[1, 2], ["foo", "bar"], [3.14, "baz", 20]]

Dann können Sie die von @ sam-mason vorgeschlagene Lösung für diese unten gezeigte Frage verwenden :

from argparse import ArgumentParser
import json

parser = ArgumentParser()
parser.add_argument('-l', type=json.loads)
parser.parse_args(['-l', '[[1,2],["foo","bar"],[3.14,"baz",20]]'])

was gibt:

Namespace(l=[[1, 2], ['foo', 'bar'], [3.14, 'baz', 20]])
Meysam Sadeghi
quelle
0

Ich möchte mehrere Listen, ganzzahlige Werte und Zeichenfolgen übergeben.

Hilfreicher Link => Wie übergebe ich eine Bash-Variable an Python?

def main(args):
    my_args = []
    for arg in args:
        if arg.startswith("[") and arg.endswith("]"):
            arg = arg.replace("[", "").replace("]", "")
            my_args.append(arg.split(","))
        else:
            my_args.append(arg)

    print(my_args)


if __name__ == "__main__":
    import sys
    main(sys.argv[1:])

Ordnung ist nicht wichtig. Wenn Sie eine Liste übergeben wollen tun, wie in zwischen "["und "]und trennen sie ein Komma.

Dann,

python test.py my_string 3 "[1,2]" "[3,4,5]"

Ausgabe => ['my_string', '3', ['1', '2'], ['3', '4', '5']], my_argsVariable enthält die Argumente der Reihe nach.

alper
quelle
0

Ich denke, die eleganteste Lösung besteht darin, eine Lambda-Funktion an "type" zu übergeben, wie von Chepner erwähnt. Wenn Sie vorher nicht wissen, wie der Begrenzer Ihrer Liste aussehen wird, können Sie außerdem mehrere Begrenzer an re.split übergeben:

# python3 test.py -l "abc xyz, 123"

import re
import argparse

parser = argparse.ArgumentParser(description='Process a list.')
parser.add_argument('-l', '--list',
                    type=lambda s: re.split(' |, ', s),
                    required=True,
                    help='comma or space delimited list of characters')

args = parser.parse_args()
print(args.list)


# Output: ['abc', 'xyz', '123']
Verneblig
quelle
Meinten Sie -lim Beispielaufruf? Woher kam -nes?
Anthony
Außerdem funktioniert die Lösung in Python 3.8.2 nicht für mich. Hier ist der Code : parser.add_argument('-l', '--list', type = lambda s: re.split('[ ,;]', s)). Hier ist die Eingabe : script.py -l abc xyz, abc\nxyz. Zum Schluss noch das Ergebnis:script.py: error: unrecognized arguments: xyz, abcnxyz
Anthony
Ändere mein Beispiel so, dass es funktioniert :)
Nebulastic