Exemple simple d'argparse souhaité: 1 argument, 3 résultats

529

La documentation du module python argparse , bien qu'excellente, j'en suis sûr, est trop pour mon petit cerveau de débutant à saisir en ce moment. Je n'ai pas besoin de faire des calculs sur la ligne de commande ou de me mêler des lignes de formatage à l'écran ou de changer les caractères des options. Tout ce que je veux faire est "Si arg est A, faites ceci, si B fait cela, si aucune des réponses ci-dessus ne montre de l'aide et quittez" .

Matt Wilkie
la source
15
puis vérifiez sys.argvl'argument que vous voulez ...
JBernardo
10
Avez-vous déjà essayé le plac ? C'est un wrapper facile à utiliser sur argparse avec une excellente documentation .
kirbyfan64sos
157
ce n'est pas toi. c'est de l'argparse. il essaie de vous emmener dans un voyage vers les étoiles et ne se soucie pas où vous vous dirigiez.
Florian Heigl,
11
Encore des API "pythoniques" folles: /
mlvljr
69
Bénissez-vous Matt Wilkie, pour avoir défendu les cerveaux débutants partout dans le monde.
polka

Réponses:

255

Ma compréhension de la question d'origine est double. Tout d'abord, en termes d'exemple d'argparse le plus simple possible, je suis surpris de ne pas l'avoir vu ici. Bien sûr, pour être très simple, tout cela est également une surcharge avec peu de puissance, mais cela pourrait vous aider à démarrer.

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("a")
args = parser.parse_args()

if args.a == 'magic.name':
    print 'You nailed it!'

Mais cet argument positionnel est désormais requis. Si vous l'omettez lors de l'appel de ce programme, vous obtiendrez une erreur sur les arguments manquants. Cela m'amène à la deuxième partie de la question d'origine. Matt Wilkie semble vouloir un seul argument facultatif sans étiquette nommée (les étiquettes --option). Ma suggestion serait de modifier le code ci-dessus comme suit:

...
parser.add_argument("a", nargs='?', default="check_string_for_empty")
...
if args.a == 'check_string_for_empty':
    print 'I can tell that no argument was given and I can deal with that here.'
elif args.a == 'magic.name':
    print 'You nailed it!'
else:
    print args.a

Il peut bien y avoir une solution plus élégante, mais cela fonctionne et est minimaliste.

mayypile
la source
4
Après un certain temps de réflexion, je conclus que cette question répond en fait mieux au Q tel que demandé et à la situation difficile dans laquelle j'étais à l'époque. Les autres excellentes réponses ont recueilli plus que suffisamment de représentants pour prouver leur valeur et peuvent résister à un peu de concurrence. :-)
matt wilkie
@badnack: C'est ce que vous voulez que ce soit, ce que 'a' représente. Si vous attendez un argument, un nom de fichier par exemple, c'est ce qui a été entré comme nom de fichier sur la ligne de commande. Vous pourriez alors faire votre propre traitement pour déterminer s'il existe dans le système de fichiers, mais c'est une autre Q&R.
mightypile
363

Voici comment je le fais argparse(avec plusieurs arguments):

parser = argparse.ArgumentParser(description='Description of your program')
parser.add_argument('-f','--foo', help='Description for foo argument', required=True)
parser.add_argument('-b','--bar', help='Description for bar argument', required=True)
args = vars(parser.parse_args())

args sera un dictionnaire contenant les arguments:

if args['foo'] == 'Hello':
    # code here

if args['bar'] == 'World':
    # code here

Dans votre cas, ajoutez simplement un seul argument.

Diego Navarro
la source
3
comme mentionné dans mon commentaire à l'autre réponse, je voudrais conserver le formatage automatique de l'aide d'argparse, mais il ne semble pas y avoir d'option pour avoir un argument sans nom (plus probablement je ne le comprends tout simplement pas quand je le vois) ), par exemple, il faut faire foo.py --action installou foo.py --action removeau lieu de simplementfoo.py install
matt wilkie
7
@mattwilkie Ensuite, vous devez définir un argument positionnel comme celui-ci: parser.add_argument('install', help='Install the app') (Remarquez que vous ne pouvez pas définir un argument positionnel avec required=True)
Diego Navarro
32
En tant que noob à argparse, cette réponse a vraiment aidé parce que je ne savais pas où trouver les options après leur adoption . En d'autres termes, j'avais besoin de comprendre comment le argsdict était généré comme ci-dessus.
mrKelley
3
Utilisez le «formulaire court» lorsque vous appelez un programme directement à partir de la ligne de commande et le «formulaire long» lorsque vous exécutez un programme / une commande dans un script. Dans ce cas, il est plus lisible par l'homme avec la forme longue et donc plus facile à suivre la logique du code / script.
ola
17
Personnellement, je trouve plus propre d'accéder aux arguments au lieu args.fooet à la args.barplace de la syntaxe du dictionnaire. Quoi qu'il en soit, bien sûr, mais args n'est pas en fait un dictionnaire mais un argparse.Namespaceobjet.
Michael Mior
210

La argparsedocumentation est raisonnablement bonne mais laisse de côté quelques détails utiles qui pourraient ne pas être évidents. (@Diego Navarro a déjà mentionné une partie de cela, mais je vais essayer de développer légèrement sa réponse.) L'utilisation de base est la suivante:

parser = argparse.ArgumentParser()
parser.add_argument('-f', '--my-foo', default='foobar')
parser.add_argument('-b', '--bar-value', default=3.14)
args = parser.parse_args()

L'objet dont vous récupérez parse_args()est un objet 'Namespace': un objet dont les variables membres sont nommées d'après vos arguments de ligne de commande. L' Namespaceobjet est la façon dont vous accédez à vos arguments et aux valeurs qui leur sont associées:

args = parser.parse_args()
print args.my_foo
print args.bar_value

(Notez que argparseremplace «-» dans vos noms d'argument par des traits de soulignement lors du nommage des variables.)

Dans de nombreuses situations, vous souhaiterez peut-être utiliser des arguments simplement comme des indicateurs qui ne prennent aucune valeur. Vous pouvez ajouter ceux-ci dans argparse comme ceci:

parser.add_argument('--foo', action='store_true')
parser.add_argument('--no-foo', action='store_false')

Ce qui précède créera des variables nommées 'foo' avec la valeur True et 'no_foo' avec la valeur False, respectivement:

if (args.foo):
    print "foo is true"

if (args.no_foo is False):
    print "nofoo is false"

Notez également que vous pouvez utiliser l'option "requis" lors de l'ajout d'un argument:

parser.add_argument('-o', '--output', required=True)

De cette façon, si vous omettez cet argument sur la ligne de commande, argparseil vous dira qu'il est manquant et arrêtera l'exécution de votre script.

Enfin, notez qu'il est possible de créer une structure de dict de vos arguments en utilisant la varsfonction, si cela vous facilite la vie.

args = parser.parse_args()
argsdict = vars(args)
print argsdict['my_foo']
print argsdict['bar_value']

Comme vous pouvez le voir, varsrenvoie un dict avec vos noms d'argument sous forme de clés et leurs valeurs sous forme de valeurs.

Il existe de nombreuses autres options et choses que vous pouvez faire, mais cela devrait couvrir les scénarios d'utilisation courants les plus essentiels.

DMH
la source
3
A quoi sert le '-f'et '-b'? Pourquoi ne pouvez-vous pas omettre cela?
user2763361
13
Il est assez conventionnel d'avoir à la fois une version «courte» (un tiret) et «longue» (deux tirets) pour chaque option d'exécution. Vous le verrez, par exemple, dans presque tous les utilitaires Unix / Linux standard; faites un man cpou man lset vous constaterez que de nombreuses options sont disponibles dans les deux versions (par exemple -f, --force). Il y a probablement des raisons très différentes pour lesquelles les gens préfèrent l'un ou l'autre, mais dans tous les cas, il est assez courant de rendre les deux formulaires disponibles dans votre programme.
DMH
59

Matt pose des questions sur les paramètres de position dans argparse, et je conviens que la documentation Python manque sur cet aspect. Il n'y a pas un seul exemple complet dans les ~ 20 pages impaires qui montre à la fois l' analyse et l'utilisation des paramètres de position .

Aucune des autres réponses ici ne montre non plus un exemple complet de paramètres de position, alors voici un exemple complet:

# tested with python 2.7.1
import argparse

parser = argparse.ArgumentParser(description="An argparse example")

parser.add_argument('action', help='The action to take (e.g. install, remove, etc.)')
parser.add_argument('foo-bar', help='Hyphens are cumbersome in positional arguments')

args = parser.parse_args()

if args.action == "install":
    print("You asked for installation")
else:
    print("You asked for something other than installation")

# The following do not work:
# print(args.foo-bar)
# print(args.foo_bar)

# But this works:
print(getattr(args, 'foo-bar'))

Ce qui m'a dérouté, c'est que argparse convertira l'argument nommé "--foo-bar" en "foo_bar", mais un paramètre de position nommé "foo-bar" reste comme "foo-bar", ce qui rend moins évident la façon de utilisez-le dans votre programme.

Remarquez les deux lignes vers la fin de mon exemple - aucune de celles-ci ne fonctionnera pour obtenir la valeur du paramètre de position foo-bar. Le premier est évidemment faux (c'est une expression arithmétique args.foo minus bar), mais le second ne fonctionne pas non plus:

AttributeError: 'Namespace' object has no attribute 'foo_bar'

Si vous souhaitez utiliser l' foo-barattribut, vous devez utiliser getattr, comme on le voit dans la dernière ligne de mon exemple. Ce qui est fou, c'est que si vous essayez d'utiliser dest=foo_barpour changer le nom de la propriété en quelque chose de plus facile d'accès, vous obtiendrez un message d'erreur vraiment bizarre:

ValueError: dest supplied twice for positional argument

Voici comment fonctionne l'exemple ci-dessus:

$ python test.py
usage: test.py [-h] action foo-bar
test.py: error: too few arguments

$ python test.py -h
usage: test.py [-h] action foo-bar

An argparse example

positional arguments:
  action      The action to take (e.g. install, remove, etc.)
  foo-bar     Hyphens are cumbersome in positional arguments

optional arguments:
  -h, --help  show this help message and exit

$ python test.py install foo
You asked for installation
foo
Mark E. Haase
la source
5
nargs='?'est l'incantation d'un "positionnel optionnel" selon stackoverflow.com/questions/4480075/…
MarkHu
Le fait qu'un positionnel foo-barne soit pas transformé en foo_barest traité dans bugs.python.org/issue15125 .
hpaulj
2
Je pense qu'une solution de contournement plus facile pour ce bogue consiste à simplement appeler l'argument "foo_bar" au lieu de "foo-bar", puis print args.foo_barfonctionne. Comme il s'agit d'un argument positionnel, vous n'avez pas besoin de spécifier le nom lors de l'appel du script, donc cela n'a pas d'importance pour l'utilisateur.
luator
@luator Vous avez raison, il est facile de renommer l'argument, mais l'auteur du rapport de bogue montre bien qu'il s'agit toujours d'une erreur en raison de la charge cognitive inutile. Lorsque vous utilisez argparse, il faut faire une pause et rappeler les différentes conventions de dénomination pour les options et les arguments. Voir bugs.python.org/msg164968 .
Mark E. Haase du
1
@mehaase Je suis totalement d'accord qu'il s'agit d'une erreur qui devrait être corrigée. Je pense simplement que renommer l'argument est la solution de contournement la plus facile et la moins déroutante que d'avoir à utiliser getattr(il est également plus flexible car il vous permet de changer un argument de facultatif en positionnel sans avoir à changer le code qui utilise la valeur).
luator
22

Encore une autre introduction sommaire, inspirée de ce post .

import argparse

# define functions, classes, etc.

# executes when your script is called from the command-line
if __name__ == "__main__":

    parser = argparse.ArgumentParser()
    #
    # define each option with: parser.add_argument
    #
    args = parser.parse_args() # automatically looks at sys.argv
    #
    # access results with: args.argumentName
    #

Les arguments sont définis avec des combinaisons des éléments suivants:

parser.add_argument( 'name', options... )              # positional argument
parser.add_argument( '-x', options... )                # single-char flag
parser.add_argument( '-x', '--long-name', options... ) # flag with long name

Les options courantes sont:

  • help : description de cet argument quand --helpest utilisé.
  • par défaut : valeur par défaut si l'argument est omis.
  • tapez : si vous attendez un floatou int(sinon c'est le cas str).
  • dest : donner un nom différent à un drapeau (par exemple '-x', '--long-name', dest='longName').
    Remarque: par défaut --long-nameest accessible avecargs.long_name
  • action : pour un traitement spécial de certains arguments
    • store_true, store_false: pour les arguments booléens
      '--foo', action='store_true' => args.foo == True
    • store_const: à utiliser avec optionconst
      '--foo', action='store_const', const=42 => args.foo == 42
    • count: pour les options répétées, comme dans./myscript.py -vv
      '-v', action='count' => args.v == 2
    • append: pour les options répétées, comme dans./myscript.py --foo 1 --foo 2
      '--foo', action='append' => args.foo == ['1', '2']
  • requis : si un indicateur est requis, ou si un argument positionnel ne l'est pas.
  • nargs : pour qu'un drapeau capture N args
    ./myscript.py --foo a b => args.foo = ['a', 'b']
  • choix : pour restreindre les entrées possibles (spécifiez comme liste de chaînes, ou entiers si type=int).
Jonathan H
la source
12

Notez le Tutoriel Argparse dans les HOWTO Python . Il part de la plupart des exemples de base, comme celui-ci:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
                    help="display a square of a given number")
args = parser.parse_args()
print(args.square**2)

et progresse vers les moins basiques.

Il y a un exemple avec un choix prédéfini pour une option, comme ce qui est demandé:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
                    help="display a square of a given number")
parser.add_argument("-v", "--verbosity", type=int, choices=[0, 1, 2],
                    help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
    print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity == 1:
    print("{}^2 == {}".format(args.square, answer))
else:
    print(answer)
Alexey
la source
C'est agréable de voir que les documents ont été mis à jour. Je vous assure que ce n'était pas le cas lorsque OP a posté la question il y a 5 ans.
ntwrkguru
10

Voici ce que j'ai trouvé dans mon projet d'apprentissage grâce principalement à @DMH ...

Code de démonstration:

import argparse

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('-f', '--flag', action='store_true', default=False)  # can 'store_false' for no-xxx flags
    parser.add_argument('-r', '--reqd', required=True)
    parser.add_argument('-o', '--opt', default='fallback')
    parser.add_argument('arg', nargs='*') # use '+' for 1 or more args (instead of 0 or more)
    parsed = parser.parse_args()
    # NOTE: args with '-' have it replaced with '_'
    print('Result:',  vars(parsed))
    print('parsed.reqd:', parsed.reqd)

if __name__ == "__main__":
    main()

Cela peut avoir évolué et est disponible en ligne: command-line.py

Script pour donner un entraînement à ce code: command-line-demo.sh

Peter L
la source
2
Enfin un exemple d'argument qui a du sens
opentokix
5

Vous pouvez également utiliser plac (un wrapper autour argparse).

En prime, il génère des instructions d'aide soignées - voir ci-dessous.

Exemple de script:

#!/usr/bin/env python3
def main(
    arg: ('Argument with two possible values', 'positional', None, None, ['A', 'B'])
):
    """General help for application"""
    if arg == 'A':
        print("Argument has value A")
    elif arg == 'B':
        print("Argument has value B")

if __name__ == '__main__':
    import plac
    plac.call(main)

Exemple de sortie:

Aucun argument fourni - example.py :

usage: example.py [-h] {A,B}
example.py: error: the following arguments are required: arg

Argument inattendu fourni - example.py C :

usage: example.py [-h] {A,B}
example.py: error: argument arg: invalid choice: 'C' (choose from 'A', 'B')

Argument correct fourni - example.py A :

Argument has value A

Menu d'aide complet (généré automatiquement) - example.py -h :

usage: example.py [-h] {A,B}

General help for application

positional arguments:
  {A,B}       Argument with two possible values

optional arguments:
  -h, --help  show this help message and exit

Brève explication:

Le nom de l'argument est généralement égal au nom du paramètre ( arg).

L'annotation de tuple après le argparamètre a la signification suivante:

  • Description ( Argument with two possible values)
  • Type d'argument - l'un de 'flag', 'option' ou 'positional' ( positional)
  • Abréviation ( None)
  • Type de valeur d'argument - par exemple. float, string ( None)
  • Ensemble de choix restreint ( ['A', 'B'])

Documentation:

Pour en savoir plus sur l'utilisation de plac, consultez sa superbe documentation:

Plac: analyser la ligne de commande en toute simplicité

quasoft
la source
4

Pour ajouter à ce que d'autres ont déclaré:

J'aime généralement utiliser le paramètre «dest» pour spécifier un nom de variable, puis utiliser «globals (). Update ()» pour placer ces variables dans l'espace de noms global.

Usage:

$ python script.py -i "Hello, World!"

Code:

...
parser.add_argument('-i', '--input', ..., dest='inputted_variable',...)
globals().update(vars(parser.parse_args()))
...
print(inputted_variable) # Prints "Hello, World!"
Sandy Chapman
la source
argparseUtilise en interne getattret setattrpour accéder aux valeurs dans l'espace de noms. De cette façon, il n'est pas gêné par des destvaleurs étrangement formées .
hpaulj
1

Un moyen très simple d'utiliser argparse et de modifier les commutateurs '-h' / '--help' pour afficher vos propres instructions d'aide sur le code personnel consiste à définir l'aide par défaut sur False, vous pouvez également ajouter autant de .add_arguments supplémentaires que vous le souhaitez. :

import argparse

parser = argparse.ArgumentParser(add_help=False)

parser.add_argument('-h', '--help', action='help',
                help='To run this script please provide two arguments')
parser.parse_args()

Exécutez: python test.py -h

Production:

usage: test.py [-h]

optional arguments:
  -h, --help  To run this script please provide two arguments
Clapier
la source
-1

La réponse la plus simple!

PS celui qui a écrit le document d'argparse est idiot

code python:

import argparse
parser = argparse.ArgumentParser(description='')
parser.add_argument('--o_dct_fname',type=str)
parser.add_argument('--tp',type=str)
parser.add_argument('--new_res_set',type=int)
args = parser.parse_args()
o_dct_fname = args.o_dct_fname
tp = args.tp
new_res_set = args.new_res_set

code en cours d'exécution

python produce_result.py --o_dct_fname o_dct --tp father_child --new_res_set 1
gouxute
la source
Cette réponse n'ajoute rien de nouveau / différent des réponses existantes.
NVS Abhilash
je suis le plus clair, parler n'est pas cher, montrer le code
gouxute