web-dev-qa-db-fra.com

Fonction d'appel basée sur argparse

Je suis nouveau à python et je joue actuellement avec lui. J'ai un script qui fait des appels d'API à un appareil. Je voudrais étendre les fonctionnalités et appeler différentes fonctions en fonction des arguments donnés lors de l'appel du script.

Actuellement, j'ai les éléments suivants:

parser = argparse.ArgumentParser()
parser.add_argument("--showtop20", help="list top 20 by app",
                    action="store_true")
parser.add_argument("--listapps", help="list all available apps",
                    action="store_true")
args = parser.parse_args()

J'ai aussi un

def showtop20():
    .....

et

def listapps():
....

Comment puis-je appeler la fonction (et seulement cela) en fonction de l'argument donné? Je ne veux pas courir

if args.showtop20:
   #code here

if args.listapps:
   #code here

car je veux déplacer les différentes fonctions vers un module plus tard en gardant le fichier exécutable principal propre et bien rangé.

26
f0rd42

Comme il semble que vous souhaitiez exécuter une et une seule fonction en fonction des arguments fournis, je vous suggère d'utiliser un argument positionnel obligatoire ./prog command, au lieu d'arguments facultatifs (./prog --command1 ou ./prog --command2).

donc, quelque chose comme ça devrait le faire:

FUNCTION_MAP = {'top20' : my_top20_func,
                'listapps' : my_listapps_func }

parser.add_argument('command', choices=FUNCTION_MAP.keys())

args = parser.parse_args()

func = FUNCTION_MAP[args.command]
func()
24
Hannes Ovrén

Il existe de nombreuses façons d'écorcher ce chat. En voici un qui utilise action='store_const' (Inspiré de l'exemple de sous-analyseur documenté):

p=argparse.ArgumentParser()
p.add_argument('--cmd1', action='store_const', const=lambda:'cmd1', dest='cmd')
p.add_argument('--cmd2', action='store_const', const=lambda:'cmd2', dest='cmd')

args = p.parse_args(['--cmd1'])
# Out[21]: Namespace(cmd=<function <lambda> at 0x9abf994>)

p.parse_args(['--cmd2']).cmd()
# Out[19]: 'cmd2'
p.parse_args(['--cmd1']).cmd()
# Out[20]: 'cmd1'

Avec un dest partagé, chaque action place sa fonction (const) dans le même attribut Namespace. La fonction est invoquée par args.cmd().

Et comme dans l'exemple des sous-analyseurs documentés, ces fonctions pourraient être écrites de manière à utiliser d'autres valeurs de Namespace.

args = parse_args()
args.cmd(args)

À des fins de comparaison, voici le cas des sous-analyseurs équivalents:

p = argparse.ArgumentParser()
sp = p.add_subparsers(dest='cmdstr')
sp1 = sp.add_parser('cmd1')
sp1.set_defaults(cmd=lambda:'cmd1')
sp2 = sp.add_parser('cmd2')
sp2.set_defaults(cmd=lambda:'cmd2')

p.parse_args(['cmd1']).cmd()
# Out[25]: 'cmd1'

Comme illustré dans la documentation, les sous-analyseurs vous permettent de définir différents arguments de paramètres pour chacune des commandes.

Et bien sûr, tous ces arguments add ou instructions d'analyseur peuvent être créés en boucle sur une liste ou un dictionnaire qui associe une clé à une fonction.

Une autre considération importante - quel type d'utilisation et d'aide voulez-vous? Les différentes approches génèrent des messages d'aide très différents.

12
hpaulj

Si vos fonctions sont "assez simples", essayez le paramètre typehttps://docs.python.org/2.7/library/argparse.html#type

type = peut prendre tout appelable qui prend un seul argument de chaîne et renvoie la valeur convertie:

Dans votre exemple (même si vous n'avez pas besoin d'une valeur convertie):

parser.add_argument("--listapps", help="list all available apps",
                    type=showtop20,
                    action="store")

Ce script simple:

import argparse

def showtop20(dummy):
    print "{0}\n".format(dummy) * 5

parser = argparse.ArgumentParser()
parser.add_argument("--listapps", help="list all available apps",
                    type=showtop20,
                    action="store")
args = parser.parse_args()

Va donner:

# ./test.py --listapps test
test
test
test
test
test
test
6
Juan Diego Godoy

Au moins d'après ce que vous avez décrit, --showtop20 et --listapps ressemble plus à des sous-commandes qu'à des options. En supposant que c'est le cas, nous pouvons utiliser des sous-analyseurs pour obtenir le résultat souhaité. Voici une preuve de concept:

import argparse
import sys

def showtop20():
    print('running showtop20')

def listapps():
    print('running listapps')

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

# Create a showtop20 subcommand    
parser_showtop20 = subparsers.add_parser('showtop20', help='list top 20 by app')
parser_showtop20.set_defaults(func=showtop20)

# Create a listapps subcommand       
parser_listapps = subparsers.add_parser('listapps', help='list all available apps')
parser_listapps.set_defaults(func=listapps)

# Print usage message if no args are supplied.

# NOTE: Python 2 will error 'too few arguments' if no subcommand is supplied.
#       No such error occurs in Python 3, which makes it feasible to check
#       whether a subcommand was provided (displaying a help message if not).
#       argparse internals vary significantly over the major versions, so it's
#       much easier to just override the args passed to it.

if len(sys.argv) <= 1:
    sys.argv.append('--help')

options = parser.parse_args()

# Run the appropriate function (in this case showtop20 or listapps)
options.func()

# If you add command-line options, consider passing them to the function,
# e.g. `options.func(options)`
6
Six