web-dev-qa-db-fra.com

option argparse pour passer une liste en tant qu'option

J'essaie de passer une liste comme argument à un programme en ligne de commande. Existe-t-il une option argparse permettant de transmettre une liste en tant qu'option?

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

Le script s'appelle comme ci-dessous

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

TL; DR

Utilisez l'option nargs ou le paramètre 'append' de l'option action (selon le comportement de l'interface utilisateur).

nargues

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

nargs='+' prend un ou plusieurs arguments, nargs='*' prend zéro ou plus.

annexe

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

Avec append, vous fournissez l’option plusieurs fois pour construire la liste.

N'utilisez pas type=list !!! - Il n'y a probablement pas de situation où vous voudriez utiliser type=list avec argparse. Déjà.


Examinons plus en détail certaines des différentes manières dont on pourrait essayer de le faire, ainsi que le résultat final.

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)

Voici le résultat attendu:

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

À emporter :

  • Utilisez nargs ou action='append'
    • nargs peut être plus simple du point de vue de l'utilisateur, mais il peut ne pas être intuitif s'il existe des arguments de position car argparse ne peut pas dire ce qui devrait être un argument de position et ce qui appartient à la nargs ; Si vous avez des arguments de position, alors action='append' pourrait être un meilleur choix.
    • Ce qui précède n’est vrai que si nargs reçoit '*', '+' ou '?'. Si vous fournissez un nombre entier (tel que 4), le mélange des options avec nargs et des arguments de positionnement ne posera aucun problème, car argparse saura exactement combien de valeurs il faut attendre pour l'option.
  • N'utilisez pas de guillemets sur la ligne de commande1
  • N'utilisez pas type=list, car il renverra une liste de listes
    • Ceci est dû au fait que sous le capot, argparse utilise la valeur de type pour contraindre chaque argument donné vous avez choisi type, pas l’agrégation de tous les arguments.
    • Vous pouvez utiliser type=int (ou autre) pour obtenir une liste d'ints (ou autre)

1: Je ne veux pas dire en général .. je veux dire utiliser des guillemets pour passer une liste à argparse n'est pas ce que vous voulez.

664
SethMMorton

Je préfère passer une chaîne délimitée que j'analyserai plus tard dans le script. Les raisons en sont la liste peut être de n'importe quel type int ou str, et parfois en utilisant nargs Je rencontre des problèmes s'il existe plusieurs arguments facultatifs et arguments de position.

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

Ensuite,

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

ou,

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

fonctionnera bien. Le délimiteur peut également être un espace, qui appliquerait des guillemets autour de la valeur de l'argument, comme dans l'exemple de la question.

60
dojuba

En plus de nargs , vous pouvez utiliser choices si vous connaissez le liste à l'avance:

>>> 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')
16
Martin Thoma

Utilisation de paramètre nargs dans la méthode add_argument de argparse

J'utilise nargs = '' comme paramètre add_argument. J'ai spécifiquement utilisé nargs = '' pour choisir les valeurs par défaut si je ne transmets aucun argument explicite

Y compris un extrait de code à titre d'exemple:

Exemple: temp_args1.py

Remarque: L'exemple de code ci-dessous est écrit en python3. En modifiant le format de l'instruction print, peut s'exécuter en python2

    #!/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))

Remarque: je collecte plusieurs arguments de chaîne qui sont stockés dans la liste - opts.alist Si vous souhaitez une liste d'entiers, modifiez le paramètre type de parser.add_argument en int

Résultat d'exécution:

    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']
5
Py_minion

Si vous souhaitez qu'un seul commutateur prenne plusieurs paramètres, utilisez nargs='+'. Si votre exemple '-l' prend réellement des entiers:

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

Produit

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

Si vous spécifiez le même argument plusieurs fois, l'action par défaut ('store') remplace les données existantes.

L'alternative consiste à utiliser l'action 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(' '))

Qui produit

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

Ou vous pouvez écrire un gestionnaire/une action personnalisée pour analyser les valeurs séparées par des virgules afin de pouvoir effectuer les opérations suivantes:

-l 123,234,345 -l 456
4
kfsone

Dans add_argument(), type n'est qu'un objet appelable qui reçoit une chaîne et retourne une valeur d'option.

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

Cela permettra de:

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

Peut-être la réponse la plus simple

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(" "))
2
FootAdministration

Si vous avez une liste imbriquée dans laquelle les listes internes ont des types et des longueurs différents et que vous souhaitez conserver le type, par exemple,

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

alors vous pouvez utiliser la solution proposée par @ sam-mason à cette question , ci-dessous:

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

qui donne:

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