multiple - python parse arguments




opzione argparse per il passaggio di un elenco come opzione (5)

Forse la risposta più semplice

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-l", "--tolist", help="input to list", action="store_true")
parser.add_argument("newlist", type=str, help="generate a list")
args = parser.parse_args()
if args.tolist:
    print(args.newlist.split(" "))

Sto provando a passare una lista come argomento ad un programma a riga di comando. Esiste un'opzione argparse per passare un elenco come opzione?

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

Lo script è chiamato come di seguito

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

In add_argument() , type è solo un oggetto callable che riceve una stringa e restituisce il valore dell'opzione.

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")

Questo permetterà di:

$ ./tool --list "[1,2,3,4]"

Preferisco passare una stringa delimitata che analizzerò più avanti nella sceneggiatura. Le ragioni di ciò sono; la lista può essere di qualsiasi tipo int o str , e a volte usando nargs mi nargs in problemi se ci sono più argomenti opzionali e argomenti posizionali.

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

Poi,

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

o,

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

funzionerà bene Il delimitatore può anche essere uno spazio, che tuttavia imporrà le virgolette attorno al valore dell'argomento come nell'esempio nella domanda.


Se intendi effettuare un singolo passaggio con più parametri, allora usi nargs='+' . Se il tuo esempio '-l' sta effettivamente prendendo interi:

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

produce

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

Se si specifica lo stesso argomento più volte, l'azione predefinita ( 'store' ) sostituisce i dati esistenti.

L'alternativa è usare l'azione append :

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

Che produce

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

Oppure puoi scrivere un gestore / azione personalizzato per analizzare i valori separati da virgola in modo che tu possa farlo

-l 123,234,345 -l 456

TL; DR

Utilizzare l'opzione nargs o l'impostazione 'append' dell'opzione di action (a seconda di come si desidera che l'interfaccia utente si comporti).

nargs

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

nargs='+' richiede 1 o più argomenti, nargs='*' richiede zero o più.

aggiungere

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

Con append si fornisce l'opzione più volte per creare l'elenco.

Non usare type=list !!! - Probabilmente non c'è alcuna situazione in cui si vorrebbe usare type=list con argparse . Mai.

Diamo un'occhiata più in dettaglio ad alcuni dei diversi modi in cui si potrebbe provare a fare questo, e il risultato finale.

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)

Ecco l'output che puoi aspettarti:

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

Takeaways :

  • Usa nargs o action='append'
    • nargs può essere più semplice dal punto di vista dell'utente, ma può non essere intuitivo se ci sono argomenti posizionali perché argparse non può dire quale dovrebbe essere un argomento posizionale e cosa appartiene ai nargs ; se hai argomenti posizionali allora action='append' potrebbe finire per essere una scelta migliore.
    • Quanto sopra è vero solo se a nargs viene dato '*' , '+' o '?' . Se fornisci un numero intero (come 4 ), non ci saranno problemi nel mixare opzioni con nargs e argomenti posizionali perché argparse saprà esattamente quanti valori aspettarsi per l'opzione.
  • Non utilizzare le virgolette sulla riga di comando 1
  • Non usare type=list , poiché restituirà un elenco di liste
    • Questo accade perché sotto la cappa, argparse usa il valore di type per forzare ogni singolo argomento dato al type scelto, non l'aggregato di tutti gli argomenti.
    • Puoi usare type=int (o qualsiasi altra cosa) per ottenere una lista di ints (o qualsiasi altra cosa)

1 : Non intendo in generale .. Voglio dire che usare le virgolette per passare una lista a argparse non è quello che vuoi.





argparse