Es habitual que los programas puedan manejar argumentos de línea de comando. Existe además una cierta convención respecto a cómo debe comportarse un programa de línea de comando, como se muestra a continuación mediante el programa ls.
ls ./tmp
ls -l ./tmp
ls --help
Del ejemplo anterior se pueden derivar las siguientes observaciones:
Es el módulo recomendado en la biblioteca estándar de Python para analizar argumentos de línea de comando. A continuación algunos ejemplos de su funcionamiento.
El archivo programa1.py tiene el siguiente código:
(nota: las instrucciones %load no son parte del código, se usan para desplegarlo)
%load ./tmp/programa1.py
import argparse
parser = argparse.ArgumentParser()
parser.parse_args()
Si bien parece que el programa no hace nada al ejecutarse desde la línea de comando con diferentes argumentos se obtiene el comportamiento que se muestra a continuación.
!python3 ./tmp/programa1.py
!python3 ./tmp/programa1.py --help
!python3 ./tmp/programa1.py --verbose
!python3 ./tmp/programa1.py foo
Si bien correr el programa sin argumentos de línea de comando no genera ninguna salida (no se gana mucho), se puede ver que se obtuvo un mensaje de ayuda sin mucho esfuerzo, con una única opción de línea de comando por defecto. Cabe señalar además que el manejo de argumentos de línea de comando no especificados se hace correctamente.
Veamos con el siguiente ejemplo como especificar argumentos posicionales.
%load ./tmp/programa2.py
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo", help="repite la cadena especificada aquí")
args = parser.parse_args()
print(args.echo)
Y si se lo ejecuta con diferentes argumentos de línea de comando se obtiene el siguiente comportamiento.
!python3 ./tmp/programa2.py -h
!python3 ./tmp/programa2.py foo
El siguiente ejemplo hace un uso algo más elaborado del argumento de entrada.
%load ./tmp/programa3.py
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("cuadrado", help="devuelve el cuadrado del número especificado", type=int)
args = parser.parse_args()
print(args.cuadrado**2)
!python3 ./tmp/programa3.py 4
Veamos ahora como especificar argumentos opcionales.
%load ./tmp/programa4.py
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbosity", help="increase output verbosity")
args = parser.parse_args()
if args.verbosity:
print("verbosity turned on")
!python3 ./tmp/programa4.py --verbosity 1
!python3 ./tmp/programa4.py --help
!python3 ./tmp/programa4.py --verbosity
Se puede ver que el argumento es opcional y que al especificarlo se debe indicar un valor, de lo contrario devuelve un error. Pero en algunos casos puede que nos interese que los valores posibles sean solo TRUE o FALSE. Eso se puede implementar de la siguiente forma.
%load ./tmp/programa5.py
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbose", help="increase output verbosity",
action="store_true")
args = parser.parse_args()
if args.verbose:
print("verbosity turned on")
!python3 ./tmp/programa5.py --verbose
!python3 ./tmp/programa5.py --verbose 1
!python3 ./tmp/programa5.py --help
La opción se parece más a una bandera ahora (el nombre cambió de forma acorde), y no requiere que se especifique un valor. La palabra clave action permite establecer una acción, que se ejecuta solo si el argumento de línea de comando --verbose se especifica, y que consiste en asignarle el valor True. No especificar el argumento --verbose corresponde a asignarle el valor False. Por último cabe señalar que el programa ya no es capaz de procesar un valor asociado al argumento y que la función de ayuda se ve modificada levemente.
Así como existe una opción abreviada -h para especificar --help es posible definir opciones abreviadas para los argumentos opcionales que uno especifica. A continuación se modifica el ejemplo anterior para hacerlo.
%load ./tmp/programa6.py
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verbose", help="increase output verbosity",
action="store_true")
args = parser.parse_args()
if args.verbose:
print("verbosity turned on")
!python3 ./tmp/programa6.py -v
!python3 ./tmp/programa6.py -h
Los argumentos posicionales y opcionales pueden combinarse, sin importar el orden en que se especifican en la invocación. El siguiente ejemplo muestra la combinación de ambos tipos de argumentos y agrega algunas variantes más elaboradas.
%load ./tmp/programa7.py
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("cuadrado", type=int,
help="muestra el cuadrado del numero especificado como argumento")
parser.add_argument("-v", "--verbosity", type=int,
help="controla la verbosidad de la salida")
args = parser.parse_args()
respuesta = args.cuadrado**2
if args.verbosity == 2:
print("el cuadrado de {} es igual a {}".format(args.cuadrado, respuesta))
elif args.verbosity == 1:
print("{}^2 == {}".format(args.cuadrado, respuesta))
else:
print(respuesta)
!python ./tmp/programa7.py 4
!python ./tmp/programa7.py 4 -v 1
!python ./tmp/programa7.py 4 -v 2
!python ./tmp/programa7.py -v 2 4
!python ./tmp/programa7.py 4 -v 3
Pero hay un detalle que se hace evidente en la última invocación, ya que el valor especificado para la opción -v excede los valores previstos. Esto se puede mejorar definiendo un conjunto de valores para el argumento.
%load ./tmp/programa8.py
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("cuadrado", type=int,
help="muestra el cuadrado del numero especificado como argumento")
parser.add_argument("-v", "--verbosity", type=int, choices=[0, 1, 2],
help="controla la verbosidad de la salida")
args = parser.parse_args()
respuesta = args.cuadrado**2
if args.verbosity == 2:
print("el cuadrado de {} es igual a {}".format(args.cuadrado, respuesta))
elif args.verbosity == 1:
print("{}^2 == {}".format(args.cuadrado, respuesta))
else:
print(respuesta)
!python3 ./tmp/programa8.py 4 -v 2
!python3 ./tmp/programa8.py 4 -v 3
!python3 ./tmp/programa8.py -h
Y por último un ejemplo que involucra un par de argumentos posicionales y un par de argumentos opcionales.
%load ./tmp/programa9.py
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("x", type=int, help="la base")
parser.add_argument("y", type=int, help="el exponente")
parser.add_argument("-v", "--verbosity", type=int, choices=[0, 1, 2],
help="controla la verbosidad de la salida")
args = parser.parse_args()
respuesta = args.x**args.y
if args.verbosity == 2:
print("{} elevado a la {} es igual a {}".format(args.x, args.y, respuesta))
elif args.verbosity == 1:
print("{}^{} == {}".format(args.x, args.y, respuesta))
else:
print(respuesta)
!python3 ./tmp/programa9.py
!python3 ./tmp/programa9.py -h
!python3 ./tmp/programa9.py 4 2 -v 1
!python3 ./tmp/programa9.py 4 2 -v 2
Modifique el Ejercicio 8.1 (y 8.1b) para la escritura de archivos de modos que el nombre de archivo de salida sea un argumento posicional y el tipo de archivo de salida (.sco y .csv) se controle mediante un argumento opcional (escribir un .sco por defecto).