web-dev-qa-db-fra.com

Passer une liste à Python à partir de la ligne de commande

J'aimerais que mon script python soit exécuté à partir de la ligne de commande lorsque des arguments sont fournis. Cependant, l'un des arguments doit être une liste d'options spécifiques à un segment du script. L'analyse de chaînes serait-elle le seul moyen d'y parvenir en construisant réellement la liste après que la chaîne "liste de lignes de commande" ait été séparée des virgules? Si oui, comment vous y prendriez-vous? 

Exemple: -details = ['nom', 'titre', 'adresse'] 

25
Sam

Programme:

import sys, ast, getopt, types

def main(argv):            
    arg_dict={}
    switches={'li':list,'di':dict,'tu':Tuple}
    singles=''.join([x[0]+':' for x in switches])
    long_form=[x+'=' for x in switches]
    d={x[0]+':':'--'+x for x in switches}
    try:            
        opts, args = getopt.getopt(argv, singles, long_form)
    except getopt.GetoptError:          
        print "bad arg"                       
        sys.exit(2)       

    for opt, arg in opts:        
        if opt[1]+':' in d: o=d[opt[1]+':'][2:]
        Elif opt in d.values(): o=opt[2:]
        else: o =''
        print opt, arg,o
        if o and arg:
            arg_dict[o]=ast.literal_eval(arg)

        if not o or not isinstance(arg_dict[o], switches[o]):    
            print opt, arg, " Error: bad arg"
            sys.exit(2)                 

    for e in arg_dict:
        print e, arg_dict[e], type(arg_dict[e])        

if __== '__main__':
    main(sys.argv[1:])        

Ligne de commande:

python py.py --l='[1,2,3,[1,2,3]]' -d "{1:'one',2:'two',3:'three'}" --tu='(1,2,3)'

Sortie:

args:  ['--l=[1,2,3,[1,2,3]]', '-d', "{1:'one',2:'two',3:'three'}", '--tu=(1,2,3)']
tu (1, 2, 3) <type 'Tuple'>
di {1: 'one', 2: 'two', 3: 'three'} <type 'dict'>
li [1, 2, 3, [1, 2, 3]] <type 'list'>

Cet extrait de code prendra des commutateurs de commande courts ou longs tels que -l ou --li= et analysera le texte après le commutateur dans une structure de données Python telle qu'une liste, un tuple ou un dict. La structure de données analysée se termine dans un dictionnaire avec la clé de commutation longue forme. 

Utiliser ast.literal_eval est relativement sûr. Il ne peut analyser que les définitions de données python. 

28
the wolf

argparse est agréable pour cela, il est dans la bibliothèque standard à partir de 2.7 et 3.2 mais sinon, un pip install à distance.

Votre préoccupation principale de spécifier une liste de longueur variable peut être résolue en faisant en sorte que la liste soit interprétée comme un seul argument dans le shell à l'aide de guillemets (cela dépend de votre shell, je suppose):

% python prog.py 'name title address' spam

où prog.py contient

import sys
my_list = sys.argv[1].split() 
# my_list is ['name', 'title', 'address']
if 'name' in my_list:
   do_something()

ou similaire. Utilisez un argument avec split pour délimiter votre liste:

% python prog.py "you're a foo, lift the bar"

my_list = [x.strip() for x in  sys.argv[1].split(',')]
# my_list is ["you're a foo", "lift the bar"]

Mais s'il vous plaît utilisez argparse à la place; surtout si vous souhaitez utiliser des indicateurs de style -c.

Une façon d'interpréter votre question est la suivante:

"J'utilise déjà argparse, car c'est le moyen judicieux d'interpréter les arguments de ligne de commande en Python. Comment puis-je spécifier que certaines options appartiennent à une catégorie spécifique?"

Dans votre question, vous avez montré un exemple de quelque chose que les coquillages que j'utilise s'étoufferaient;

% python prog.py -v -details=['name', 'title', 'address'] --quickly -t 4

ne parviendraient pas à être analysés en python car ils utiliseraient des espaces pour séparer les arguments et pourraient utiliser [et] comme syntaxe Shell.

Je suggère le suivant

% python prog.py -v --details name title address --quickly -t 4

où un fichier prog.py de

import argparse

parser = argparse.ArgumentParser() 
parser.add_argument('-v', action='store_true')
parser.add_argument('--details', nargs='*')
parser.add_argument('--quickly', action='store_true')
parser.add_argument('-t')

args = parser.parse_args()
#args is Namespace(details=['asdf', 'a', 'a'], quickly=False, t='4', v=True)
details = args.details
#details is ['asdf', 'a', 'a']

Maintenant, selon votre question, vous n'avez pas à analyser la chaîne vous-même.

26
Thomas

Oui, argparse est votre meilleur choix. Si vous souhaitez fournir une liste de valeurs à l’un de vos arguments nommés, le résultat est le suivant (le paramètre nargs en est la clé):

>>> import argparse
>>> arg_parser = argparse.ArgumentParser()
>>> arg_parser.add_argument('--details',
                            nargs='*',
                            type=str,
                            default=[],
                            help='a list of the details')

# your args on the command line like this example
>>> the_args = arg_parser.parse_args("--details 'name' 'title' 'address'".split())
>>> print the_args.details
["'name'", "'title'", "'address'"])
3
Mark Gemmill

J'aime beaucoup l'approche de the-wolf qui utilise des collections de longueur variable comme arguments de chaîne explicites.

À mon avis, nargs='*' présente des inconvénients notables: lorsque vous tentez de collecter des chaînes en tant qu'arguments de position (au moins une chaîne doit être présente), ou si vous essayez d'utiliser des sous-vendeurs, vous constaterez que nargs='*' et nargs='+' utilisent des complétions gourmandes, et ils ne le font pas. semble pas cesser de consommer pour de bonnes raisons. Même si la syntaxe pour un argument optionnel ou un nombre apparaît, le type string () continuera à consommer. (Cela devient plus difficile à anticiper avec les sous-vendeurs).

Dans le meilleur des cas, les arguments placés après (positional et facultatif) sont ignorés et, pire encore, vous passez probablement les types de données corrupt au tableau argparse. 

Nous devrions pouvoir définir un ActionType personnalisé qui recherche une chaîne entre guillemets. S'il en trouve un, nous adaptons les exemples de the-wolf (presque textuellement, semble-t-il). 

Cela permet de garder les choses propres dans argparse et rend l'utilisation générale des collections variables beaucoup moins chiante.

0
user2097818