Ejemplo de argumento simple deseado: 1 argumento, 3 resultados

529

La documentación para el módulo argparse python , aunque excelente, estoy seguro, es demasiado para mi pequeño cerebro principiante en este momento. No necesito hacer cálculos matemáticos en la línea de comando o entrometerme con las líneas de formato en la pantalla o cambiar los caracteres de opción. Todo lo que quiero hacer es "Si arg es A, haz esto, si B hace eso, si ninguno de los anteriores muestra ayuda y renuncia" .

wilkie mate
fuente
15
entonces simplemente verifique sys.argvel argumento que desea ...
JBernardo
10
¿Alguna vez has probado plac ? Es un contenedor fácil de usar sobre argparse con excelente documentación .
kirbyfan64sos
157
no eres tu. Es argparse. trata de llevarte a un viaje a las estrellas y no le importa a dónde te diriges.
Florian Heigl
11
APIs "pitónicas" nuevamente: /
mlvljr
69
Dios te bendiga, Matt Wilkie, por defender pequeños cerebros principiantes en todas partes.
polka

Respuestas:

255

Mi comprensión de la pregunta original es doble. Primero, en términos del ejemplo argparse más simple posible, me sorprende que no lo haya visto aquí. Por supuesto, para ser absolutamente simple, también es todo sobrecarga con poca potencia, pero podría ayudarlo a comenzar.

import argparse

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

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

Pero ahora se requiere este argumento posicional. Si lo omite al invocar este programa, recibirá un error sobre la falta de argumentos. Esto me lleva a la segunda parte de la pregunta original. Matt Wilkie parece querer un único argumento opcional sin una etiqueta con nombre (las etiquetas de opción). Mi sugerencia sería modificar el código anterior de la siguiente manera:

...
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

Puede haber una solución más elegante, pero esto funciona y es minimalista.

mightypile
fuente
44
Después de reflexionar un poco, concluyo que esta pregunta en realidad responde mejor a la Q como se le preguntó y a la situación en la que estaba en ese momento. Las otras excelentes respuestas han obtenido más que suficientes representantes para demostrar su valía y pueden soportar un poco de competencia. :-)
matt wilkie
@badnack: Es lo que quieras que sea, lo que sea que represente 'a'. Si espera un argumento, un nombre de archivo, por ejemplo, es lo que se ingresó como nombre de archivo en la línea de comando. Luego puede hacer su propio procesamiento para determinar si existe en el sistema de archivos, pero eso es otra Q&A.
mightypile
363

Así es como lo hago argparse(con múltiples argumentos):

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 será un diccionario que contenga los argumentos:

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

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

En su caso, simplemente agregue un solo argumento.

Diego Navarro
fuente
3
Como se menciona en mi comentario a la otra respuesta, me gustaría mantener el formato de ayuda automática de argparse, pero no parece haber una opción para tener un argumento sin respuesta (lo más probable es que simplemente no lo entiendo cuando lo veo ), por ejemplo, uno debe hacer foo.py --action installo foo.py --action removeno simplementefoo.py install
matt wilkie
77
@mattwilkie Luego debe definir un argumento posicional como este: parser.add_argument('install', help='Install the app') (Observe que no puede definir un argumento posicional con required=True)
Diego Navarro
32
Como novato en argparse, esta respuesta realmente ayudó porque no sabía dónde encontrar las opciones después de que se pasaran . En otras palabras, necesitaba entender cómo argsse generó el dict como se indicó anteriormente.
MrKelley
3
Use la 'forma corta' cuando llame al programa directamente desde la línea de comandos y la 'forma larga' cuando ejecute un programa / comando dentro de un script. En ese caso, es más legible para los humanos con la forma larga y, por lo tanto, es más fácil seguir la lógica del código / script.
ola
17
Personalmente, me parece más claro acceder a los argumentos como args.fooy en args.barlugar de la sintaxis del diccionario. De cualquier manera, está bien, por supuesto, pero args no es en realidad un diccionario sino un argparse.Namespaceobjeto.
Michael Mior
210

La argparsedocumentación es razonablemente buena, pero deja de lado algunos detalles útiles que podrían no ser obvios. (@Diego Navarro ya mencionó algo de esto, pero trataré de ampliar un poco su respuesta). El uso básico es el siguiente:

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

El objeto del que regresa parse_args()es un objeto 'Espacio de nombres': un objeto cuyas variables miembro se nombran después de sus argumentos de línea de comandos. El Namespaceobjeto es cómo accede a sus argumentos y los valores asociados con ellos:

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

(Tenga en cuenta que argparsereemplaza '-' en los nombres de sus argumentos con guiones bajos al nombrar las variables).

En muchas situaciones, es posible que desee utilizar argumentos simplemente como indicadores que no tienen ningún valor. Puede agregarlos en argparse de esta manera:

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

Lo anterior creará variables llamadas 'foo' con valor True y 'no_foo' con valor False, respectivamente:

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

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

Tenga en cuenta también que puede usar la opción "requerida" al agregar un argumento:

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

De esa manera, si omite este argumento en la línea de comando, argparsele dirá que falta y detendrá la ejecución de su script.

Finalmente, tenga en cuenta que es posible crear una estructura dict de sus argumentos utilizando la varsfunción, si eso le facilita la vida.

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

Como puede ver, varsdevuelve un dict con sus nombres de argumentos como claves y sus valores como, er, valores.

Hay muchas otras opciones y cosas que puede hacer, pero esto debería cubrir los escenarios de uso más esenciales y comunes.

DMH
fuente
3
¿Cuál es el punto de la '-f'y '-b'? ¿Por qué no puedes omitir esto?
user2763361
13
Es bastante convencional tener una versión de 'forma corta' (un guión) y 'forma larga' (dos guiones) para cada opción de tiempo de ejecución. Verá esto, por ejemplo, en casi todas las utilidades estándar de Unix / Linux; haz una man cpo man lsy encontrarás que muchas opciones vienen en ambos sabores (por ejemplo -f, --force). Probablemente hay razones muy diversas por las cuales las personas prefieren uno u otro, pero en cualquier caso es bastante estándar hacer que ambos formularios estén disponibles en su programa.
DMH
59

Matt pregunta acerca de los parámetros posicionales en argparse, y estoy de acuerdo en que falta la documentación de Python sobre este aspecto. No hay un solo ejemplo completo en las ~ 20 páginas impares que muestre tanto el análisis como el uso de parámetros posicionales .

Ninguna de las otras respuestas aquí muestra un ejemplo completo de parámetros posicionales, así que aquí hay un ejemplo completo:

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

Lo que me desconcertó es que argparse convertirá el argumento nombrado "--foo-bar" en "foo_bar", pero un parámetro posicional llamado "foo-bar" permanece como "foo-bar", lo que hace que sea menos obvio cómo úsalo en tu programa.

Observe las dos líneas cerca del final de mi ejemplo: ninguna de ellas funcionará para obtener el valor del parámetro posicional foo-bar. La primera es obviamente incorrecta (es una expresión aritmética args.foo menos barra), pero la segunda tampoco funciona:

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

Si desea usar el foo-baratributo, debe usar getattr, como se ve en la última línea de mi ejemplo. Lo loco es que si intentas usar dest=foo_barpara cambiar el nombre de la propiedad a algo que sea más fácil de acceder, obtendrás un mensaje de error realmente extraño:

ValueError: dest supplied twice for positional argument

Así es como se ejecuta el ejemplo anterior:

$ 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
fuente
55
nargs='?'es el conjuro para un "posicional opcional" según stackoverflow.com/questions/4480075/…
MarkHu
El hecho de que un posicional foo-barno se transforma foo_barse aborda en bugs.python.org/issue15125 .
hpaulj
2
Creo que una solución más fácil para este error es simplemente llamar al argumento "foo_bar" en lugar de "foo-bar", y luego print args.foo_barfunciona. Dado que es un argumento posicional, no tiene que especificar el nombre al llamar al script, por lo que no importa para el usuario.
luator
@luator Tienes razón, es fácil cambiar el nombre del argumento, pero el autor del informe de error hace un buen caso de que esto sigue siendo un error debido a la carga cognitiva innecesaria. Cuando se usa argparse, uno debe pausar y recordar las diferentes convenciones de nomenclatura para opciones y argumentos. Ver bugs.python.org/msg164968 .
Mark E. Haase
1
@mehaase Estoy totalmente de acuerdo en que se trata de un error que debería solucionarse. Simplemente creo que cambiar el nombre del argumento es la solución más fácil y menos confusa que tener que usar getattr(también es más flexible, ya que le permite cambiar un argumento de opcional a posicional sin tener que cambiar el código que usa el valor).
luator
22

Otra introducción sumaria, inspirada en esta publicación .

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
    #

Los argumentos se definen con combinaciones de lo siguiente:

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

Las opciones comunes son:

  • ayuda : descripción de este argumento cuando --helpse usa.
  • default : valor predeterminado si se omite el argumento.
  • tipo : si espera un floato int(de lo contrario es str).
  • dest : dar un nombre diferente a una bandera (por ejemplo '-x', '--long-name', dest='longName').
    Nota: por defecto --long-namese accede conargs.long_name
  • acción : para un manejo especial de ciertos argumentos
    • store_true, store_false: para argumentos booleanos
      '--foo', action='store_true' => args.foo == True
    • store_const: para ser usado con la opciónconst
      '--foo', action='store_const', const=42 => args.foo == 42
    • count: para opciones repetidas, como en./myscript.py -vv
      '-v', action='count' => args.v == 2
    • append: para opciones repetidas, como en./myscript.py --foo 1 --foo 2
      '--foo', action='append' => args.foo == ['1', '2']
  • obligatorio : si se requiere un indicador o no se requiere un argumento posicional.
  • nargs : para que una bandera capture N args
    ./myscript.py --foo a b => args.foo = ['a', 'b']
  • opciones : para restringir posibles entradas (especificar como lista de cadenas, o ints si type=int).
Jonathan H
fuente
12

Tenga en cuenta el Tutorial de Argparse en Python HOWTOs . Comienza con la mayoría de los ejemplos básicos, como este:

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)

y progresa a los menos básicos.

Hay un ejemplo con una opción predefinida para una opción, como lo que se pide:

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
fuente
Es bueno ver que los documentos se han actualizado. Les aseguro que este no fue el caso cuando OP publicó la pregunta hace 5 años.
ntwrkguru
10

Esto es lo que se me ocurrió en mi proyecto de aprendizaje gracias principalmente a @DMH ...

Código de demostración:

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

Esto puede haber evolucionado y está disponible en línea: command-line.py

Script para darle entrenamiento a este código: command-line-demo.sh

Peter L
fuente
2
Finalmente un ejemplo argparse que tiene sentido
opentokix
5

También podrías usar plac (un envoltorio alrededorargparse ).

Como beneficio adicional, genera buenas instrucciones de ayuda: consulte a continuación.

Script de ejemplo:

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

Salida de ejemplo:

No se proporcionaron argumentos - example.py :

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

Argumento inesperado proporcionado example.py C :

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

Argumento correcto proporcionado - example.py A :

Argument has value A

Menú de ayuda completo (generado automáticamente) - 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

Breve explicación:

El nombre del argumento generalmente es igual al nombre del parámetro (arg ).

La anotación de tupla después del argparámetro tiene el siguiente significado:

  • Descripción ( Argument with two possible values)
  • Tipo de argumento: uno de 'flag', 'option' o 'positional' ( positional)
  • Abreviatura ( None)
  • Tipo de valor de argumento - ej. flotador, cadena ( None)
  • Conjunto restringido de opciones ( ['A', 'B'])

Documentación:

Para obtener más información sobre el uso de plac, consulte su excelente documentación:

Plac: Analizando la línea de comando de la manera fácil

cuasoft
fuente
4

Para agregar a lo que otros han declarado:

Por lo general, me gusta usar el parámetro 'dest' para especificar un nombre de variable y luego usar 'globals (). Update ()' para poner esas variables en el espacio de nombres global.

Uso:

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

Código:

...
parser.add_argument('-i', '--input', ..., dest='inputted_variable',...)
globals().update(vars(parser.parse_args()))
...
print(inputted_variable) # Prints "Hello, World!"
Sandy Chapman
fuente
argparseUtiliza internamente getattry setattrpara acceder a valores en el espacio de nombres. De esa manera no le molestan los destvalores formados de manera extraña .
hpaulj
1

Una forma realmente simple de usar argparse y modificar los modificadores '-h' / '--help' para mostrar sus propias instrucciones de ayuda de código personal es establecer la ayuda predeterminada en False, también puede agregar tantos .add_arguments adicionales como desee :

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

Ejecute: python test.py -h

Salida:

usage: test.py [-h]

optional arguments:
  -h, --help  To run this script please provide two arguments
Conejera
fuente
-1

La respuesta más simple!

PD: el que escribió el documento de argparse es tonto

código de 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

código de ejecución

python produce_result.py --o_dct_fname o_dct --tp father_child --new_res_set 1
gouxute
fuente
Esta respuesta no agrega nada nuevo / diferente a las respuestas existentes.
NVS Abhilash
soy el más claro, hablar es barato, mostrar el código
gouxute