Interesante es saber sobre cómo funciona el navegador web Mozilla Firefox (para nosotros, nuestro favorito) y vamos a diseccionar y explicar de manera sencilla, sin mayores pretensiones, sus diversos componentes. Para ello comenzaremos de atrás para adelante: el último Proyecto de Mozilla Firefox llamado «Quantum«, ¡Investiguemos juntos!
En el tutorial anterior sobre registro de eventos con «logging» utilizamos a «argparse« para permitir a nuestros usuarios y usarias a establecer un nivel de registro de eventos en caso de ser necesario hacer seguimiento a nuestra aplicación. Prometimos allí ahondar con un tutorial completo sobre el tema y aquí lo prometido, ¡estudiemos juntos!
Introducción.
Para no caer en la redundacia, os recomendamos leer nuestro trabajo anterior, la sección «aislada» sobre argparse. Allí colocamos como ejemplo el comando ls;en realidad casi todas las aplicaciones que corren sobre la línea de comando aceptan argumentos a la derecha conformando parte de la orden de ejecución al presionar la tecla enter o intro.
Como dijimos en nuestro anterior tutorial sobre el registro de eventos en nuestras aplicaciones, argparse proviene de optparse el cual a su vez fue «descontinuada» desde la versión Python 2.7 ¿Por qué entrecomillamos? Lo hacemos porque como es software libre cualquiera puede hacer una bifurcación «fork» y continuar desarrollandolo a su gusto y conveniencia. De hecho se lleva un desarrollo en paralelo en GithHub como adelante veremos.
Lo bueno del asunto es que son bastantes similares en cuanto a su sintaxis y esto es así para facilitar a los desarrolladores que usaron optparse y ahora necesitan la migración a argparse.
Código previo a argparse.
Pero antes de entrar de lleno en argparse y como éste es un tutorial dedicado a dicha librería, vamos a ir un poco más allá: las bases sobre las cuales funciona argparse. Ya bien lo dice Richard Stallman, padre del software libre: «Nadie, ni siquiera Beethoven podría inventar la música desde cero. Es igual con la informática».
He aquí que una de las librerías básicas en el entorno de programación Python lo es sys. Para agregarlo a nuestros programas debemos enlazarlo con el comando import sys y podremos comenzar a usar sus objetos, los cuales no estudiaremos completamente en este tutorial sino que vamos a centrarnos en uno de sus componentes: sys.argv. Por medio de éste podremos acceder a la cadena de texto completa con la que el usuario o usuaria haya invocado nuestra aplicación por medio de la línea de comando. De una vez vamos a la práctica, tras esta muy breve teoría:
import sys
print("Número de argumentos: ", len(sys.argv))
print("Los argumentos son : ", str(sys.argv))
Explicación: sys.argv es, simplemente, una lista con cada palabra (entendiendose como palabra cualquier cadena de texto delimitada por al menos un espacio) con la que se invoca el guion «script» de nuestro, o de cualquier, programa.
La primera línea «enlaza» con la librería sys, permite cargarla en memoria y nos permite acceder a sus métodos , eventos y constantes.
La segunda línea usa la función len() que obtiene el largo de la lista, osea, el número de elementos -léase palabras-con la que se invocó nuestro guion «script».
La tercera línea muestra por pantalla todos y cada uno de los elementos de la lista especial.
Lo más curioso del asunto es que podemos no solamente acceder a la lista sino que también podemos cambiar sus valores, ¡probad! Lo que si es cierto es que el primer elemento (elemento cero) será siempre el nombre del fichero que almacena el guion escrito en python, con todo y extensión (aunque si no tuviera extensión .py igual se ejecuta) y los demás elementos de la lista son los argumentos o parámetros ya sea que lo escriba el usuario o le sea pasado al programa por el comando tubería «pipe» o «|» o en una variable en un guion «script» BASH.
Instalando argparse en nuestros ordenadores.
Para eliminar todo tipo de dudas, usamos Python versión 3.X -ya lo hemos dicho en nuestras entradas anteriores, revisad- y probablemente ya tengáis instalado argparse en vuestro ordenador. Al usar el comando import argparse y de no estar instalado de inmediato sale el mensaje de error en Python por lo que podemos instalarlo de diferentes maneras.
Por medio de pip3.
Para instalar argparse por medio de pip3 debemos escribir pip install argparse con los debidos derechos de administrador y así poder descargarlo de internet. Explicamos: pip3 es un esfuerzo en reunir en un repositorio de aplicaciones oficial de muchos software hecho por terceros pero que son supervisadas de manera directa por el equipo desarrollador de Python. Para saber si tenemos instalado pip3 simplemente escribimos pip3 –version y mostrará la versión instalada (ah, y de paso mirad otro ejemplo de argumentos en una aplicación «–version«) y dado el caso que no la tengamos instalada podremos usar:
En GitHub hallaremos el repositorio de Thomas Waldmann quien claramente advierte que el desarrollo de argparse es almacenado oficialmente por el equipo de desarrollo de Python pero que él mantiene una copia para quienes tengan Python 2.X y quieran agregar argparse a sus aplicaciones. De tal manera que si vosotros no lo tenéis instalado y no queréis -o no podéis- usar pip3 pues clonad el proyecto y ejecutad setup.py
Para los que les gusta la «arqueología» de software en Google podéis deleitaros en el siguiente enlace (tal parece que años atrás estaba alojado por aquellos lares antes de ser migrado el código fuente de argparse a la Fundación Python).
Observación importante:
Si sois como nosotros que tenemos instalado tanto Python 2 como Python 3 os damos el siguiente dato: si abrís un guion o programa con Python 2 y usáis argparse se generará un archivo precompilado «.pyc» cuya finalidad es cargar más rápidamente nuestro programa en sucesivos llamados. Luego si abrís el mismo «script» con Python 3 obetendréis un mensaje de error más o menos indicando «error en magic number«. Lo que debéis hacer es simplemente borrar todos los archivos «*.pyc» -por si las dudas- que en cuanto se vuelvan a ejecutar se generarán de nuevo. Advertidos quedáis ? .
Primeros pasos con argparse.
Tan solo debemos escribir nuestro guion de la siguiente manera:
import argparse
analizador = argparse.ArgumentParser(description='Tutorial sobre argparse.')
analizador.parse_args()
Primero llamamos a la librería y luego a un ubjeto analizador le asociamos con nuestra descripción de aplicación. Luego le ordenamos que muestre por pantalla los argumentos recibidos desde la línea de comandos. Así de sencillo -y esa es la idea- con tres simples líneas tenemos acceso al pase de parámetros y opciones por la línea de comando. Al llamar sin más el guión no veremos ningún resultado -aparente-. Pero si ejecutamos lo siguiente:
python3 tutorial_argparser.py -a
Veremos la siguiente respuesta:
usage: tutorial_argparser.py [-h]
tutorial_argparser.py: error: unrecognized arguments: -a
Nota importante: nosotros guardamos el programa en un fichero llamado tutorial_arparse.py y tal vez se sientan tentados a no escribir tanto y nombrarlo simplemente argparse.py ¡No lo hagáis! Sucederá que al ejecutar el guion se llamará a si mismo primero antes que buscarlo en las librerías Python. Este es el comportamiento predeterminado para nosotros cargar nuestras propias librerías: toda «importación» buscará primero en la carpeta donde está guardado el guion. Ya sabéis entonces.
Como véis ya argparse está trabajando para nosotros. La primera línea con el encabezado «usage:» indica los argumentos válidos -en este caso opcionales ya que está encerrados entre corchetes- y vemos que tiene la opción «-h». La segunda línea nos indica que ha sucedido un error en el archivo tutorial_argparser.py e indicando que es un argumento no reconocido lo que le acabamos de escribir: «-a«.
Lo que tenemos que experimentar ahora es precisamente «correr» el programa con el argumento «-h» y como probablemente ya sabéis ése es precisamente la orden para solicitar ayuda, veamos:
python3 tutorial_argparser.py -h
Obtendremos el siguiente mensaje:
usage: tutorial_argparser.py [-h]
Tutorial sobre argparse.
optional arguments:
-h, --help show this help message and exit
De nuevo la primera línea nos muestra los argumentos disponibles. La segunda ofrece la descripción de nuestro programa, la que le indicamos al inicializar la librería. La tercera línea (obviamente las líneas en blanco no la numeramos por propósitos didácticos) nos indica lo que hace el argumento solicitado: muestra el mensaje de ayuda y sale sin ejecutar ningún otro código. Notad que incluso nos muestra una opción «larga» del argumento de ayuda: «–help«. El siguiente paso es agregar nuestro primer argumento, veamos.
Agregando nuestro primer argumento a nuestro programa.
Argumento opcional:
Para que un argumento se opcional debemos antecederlo de un guion «-«; modifiquemos nuestro fichero de la siguiente manera:
import argparse
analizador = argparse.ArgumentParser(description='Tutorial sobre argparse.')
analizador.add_argument("-a", help="Detecta y confirma nuestro primer argumento.")
argumento = analizador.parse_args()
if argumento.a:
print("Argumento opcional solicitado: -a")
print("Argumento acompañado de:"+argumento.a)
else:
print("Ningún argumento.")
Allí con el método «.add_argument» establecimos la palabra clave «-a» y colocamos una breve descripción que será mostrada al solicitar ayuda con la consabida «-h» o «–help» la cual de una buena vez pedimos y obtenemos:
usage: tutorial_argparser.py [-h] [-a A]
Tutorial sobre argparse.
optional arguments:
-h, --help show this help message and exit
-a A Detecta y confirma nuestro primer argumento
Ahora empezamos a probar el nuevo argumento, se lo pasamos a la aplicación con el comando
$ python3 tutorial_argparser.py -a
y gentilmente nos advertirá que se necesita un argumento para la opción «-a«, es decir, será opcional, pero una vez que lo llamamos debemos acompañarlo de una cadena de texto, mirad:
Argumento opcional solicitado: -a
Argumento acompañado de:¡Hola!
Es hora de acompañar el argumento «-a» de una opción larga, nemotécnica, así que establecemos que sea «–aviso«: ya uséis uno u otro el comportamiento será el mismo.
analizador.add_argument("-a", "--aviso", help="Detecta y confirma nuestro primer argumento.")
argumento = analizador.parse_args()
if argumento.aviso:
print("Argumento opcional solicitado: --aviso")
print("Argumento acompañado de:"+argumento.aviso)
else:
print("Ningún argumento.")
Notad que tuvimos que cambiar el método «.a» por «.aviso«. También debemos agregar un entrecomillado si la frase que queremos pasar contiene varias palabras, de lo contrario argparse los interpretará como si fueran varios argumentos diferentes unos de otros:
Debemos acotar que, por defecto, argparse espera que sean cadenas de texto los argumentos que le pasemos a menos que le indiquemos expresamente lo contrario. Si necesitaramos pasar algún valor numérico, y que sea interpretado como tal, debemos agregar la opción type=int en donde definimos el argumento. Para darle utilidad esto último, cambiamos para que muestre repetidamente tantas veces como indique el número que pasemos, mirad atentamente:
mport argparse
analizador = argparse.ArgumentParser(description='Tutorial sobre argparse.')
analizador.add_argument(
"-a",
"--aviso",
help="Detecta y confirma nuestro primer argumento",
type=int
)
argumento = analizador.parse_args()
if argumento.aviso:
print("Argumento opcional solicitado: --aviso")
for x in range(0, argumento.aviso):
print("Argumento acompañado de:"+str(argumento.aviso))
else:
print("Ningún argumento solicitado")
Los cambios que hicimos implican usar la función str() que convierte la variable de tipo entero numérico a cadena de texto para poder usar el ciclo «for():«, así imprimirá el mensaje tantas veces como sea solicitado.
La isntrucción «type=» es poderosa, de hecho puede albergar cualquier tipo de variable, objeto ¡e incluso una función! Por ser tan avanzada por ahora no la estudiaremos en profundidad.
Ahora vamos a ver argumentos necesarios para ejecutar nuestro guion.
Argumento obligatorio.
Muchas aplicaciones precisan de un argumento obligatorio, por ejemplo, si está diseñada para analizar y trabajar con el contenido de un fichero pues es necesario indicarle que se debe pasar un nombre de archivo. Para ello modificaremos de nuevo de esta manera:
import argparse
analizador = argparse.ArgumentParser(description='Tutorial sobre argparse.')
analizador.add_argument(
"archivo",
help="Indica el nombre del fichero a trabajar.",
)
argumento = analizador.parse_args()
if argumento.archivo="archivo":
print("Argumento OBLIGATORIO solicitado: archivo")
print("Nombre del archivo:"+argumento.archivo)
Si corremos sin parámetro alguno nos indicará que DEBEMOS indicar un nombre de fichero; si lo agregamos veremos esto:
$ python3 tutorial_argparser.py lista.txt
Argumento OBLIGATORIO solicitado: archivo
Nombre del archivo:lista.txt
Argumento obligatorio repetido n veces («nargs=n«).
Muchas veces una aplicación necesita un archivo origen de donde sacar datos, procesarlos y verter la respuestra en otro archivo: para ello podemos utilizar el siguiente código:
import argparse
analizador = argparse.ArgumentParser(description='Tutorial sobre argparse.')
analizador.add_argument(
"nom_arch",
nargs=2,
help="Indica el nombre de los ficheros origen y destino a trabajar.",
)
argumento = analizador.parse_args()
if argumento.nom_arch:
print("Argumento OBLIGATORIO solicitado: nom_arch")
print("Nombres de los archivos:")
print(argumento.nom_arch[0])
print(argumento.nom_arch[1])
Observad la línea nargs=2: le estamos indicando que necesita dos argumentos (o los que necesitemos) , la desventaja de este método es que al usuario colocar un solo argumento argparse emite un mensaje que puede ser confuso, no es un mensaje explícito (recordad las reglas de oro de Python: explícito es mejor que implícito), es decir:
$ python3 tutorial_argparser.py arch1
usage: tutorial_argparser.py [-h] nom_arch nom_arch
tutorial_argparser.py: error: the following arguments are required: nom_arch
Como véis repite lo mismo n veces cuando la cantidad de argumentos NO coincide con nargs. La ventaja acá es que codificamos menos porque no tenemos que incluir dos parámetros con diferentes nombres pero dejemos aparte la flojera, seamos explícitos:
import argparse
analizador = argparse.ArgumentParser(description='Tutorial sobre argparse.')
analizador.add_argument(
"nom_arch_orig",
help="Indica el nombre del archivo origen.",
)
analizador.add_argument(
"nom_arch_dest",
help="Indica el nombre del archivo destino.",
)
argumento = analizador.parse_args()
if argumento.nom_arch_orig:
print("Argumentos OBLIGATORIOS solicitados: nom_arch_orig y nom_arch_dest")
print("Nombres de los archivos:")
print(argumento.nom_arch_orig)
print(argumento.nom_arch_dest)
Así es menos confuso para nuestros usuarios y usuarias:
$ python3 tutorial_argparser.py
usage: tutorial_argparser.py [-h] nom_arch_orig nom_arch_dest
tutorial_argparser.py: error: the following arguments are required: nom_arch_orig, nom_arch_dest
$ python3 tutorial_argparser.py arch_orig.txt arch_dest.txt
Argumentos OBLIGATORIOS solicitados: nom_arch_orig y nom_arch_dest
Nombres de los archivos:
arch_orig.txt
arch_dest.txt
¿En cuales condiciones nos es útil nargs en modo múltiple? Ahora no viene nada a la cabeza pero alguna utilidad de seguro tendrá.
Ningún argumento, uno o más argumentos (» nargs=‘*’ «).
Por otro lado, así como nargs especifica un número exacto de argumentos, también permite el caracter asterisco que funciona a modo de comodín: puede aceptar uno, dos o más argumentos –o ninguno–. Escribamos este código:
import argparse
analizador = argparse.ArgumentParser(description='Tutorial sobre argparse.')
analizador.add_argument(
"nombres",
nargs="*",
help="Recibe una lista de nombres de personas.",
)
argumento = analizador.parse_args()
if argumento.nombres:
print("Argumento(s) OBLIGATORIO(S) solicitado(s): nombres")
print("Nombres de las personas:")
print(argumento.nombres)
Y probemos su salida:
$ python3 tutorial_argparser.py
$ python3 tutorial_argparser.py José
Argumento(s) solicitado(s): nombres
Nombres de las personas:
['José']
$ python3 tutorial_argparser.py José María Pedro Carmen
Argumento(s) solicitado(s): nombres
Nombres de las personas:
['José', 'María', 'Pedro', 'Carmen']
En la primera línea del terminal notamos que no necesita argumento alguno para funcionar, eso sería «cero o más». Avizorad que si necesitamos por lo menos una persona en la lista podemos utilizar el signo de suma «+» en vez del asterisco («uno o más»), y al sustituirlo y ejecutar el programa veremos lo siguiente:
$ python3 tutorial_argparser.py
usage: tutorial_argparser.py [-h] nombres [nombres ...]
tutorial_argparser.py: error: the following arguments are required: nombres
Así nos dice que «nombres» necesita al menos uno (no está entre corchetes, es obligatorio) y que podemos agregar otros nombres de personas, esto lo indica entre corchetes y con tres puntos suspensivos.
Un argumento no obligatorio ya que utiliza un valor por defecto (» nargs=‘?’ «).
En este caso se utiliza nargs=»?» en combinación de un valor por defecto default=’cadena_de_texto’ por lo que esta opción es un tanto extraña no es obligatoria ya que si no se le pasa un valor toma el que por defecto le pongamos, este ejemplo ilustra muy bien lo que decimos:
import argparse
analizador = argparse.ArgumentParser(description='Tutorial sobre argparse.')
analizador.add_argument(
"nombre",
nargs="?",
default='Jesús',
help="Recibe un nombre de persona y si no es suministrado utiliza 'Jesús'.",
)
argumento = analizador.parse_args()
if argumento.nombre:
print("Argumento: solicita un nombre (por defecto utiliza 'Jesús')")
print("Nombre:")
print(argumento.nombre)
Ahora bien, al ejecutarlo fijáos bien en lo que hace:
$ python3 tutorial_argparser.py
Argumento: solicita un nombre (por defecto utiliza 'Jesús')
Nombre:
Jesús
$ python3 tutorial_argparser.py Pedro
Argumento: solicita un nombre (por defecto utiliza 'Jesús')
Nombre:
Pedro
Esta opción es tremendamente útil si le pedimos a la usuaria que indique un archivo de origen y, si lo desea, un archivo destino. De no colocar un archivo destino entonces utilizará el nombre de archivo que nosotros mismo escojamos (y si ese archivo existe bien le podemos agregar datos al final o creamos un archivo nuevo con el nombre por defecto acompañado de un número que esté libre: arch1, arch2, … arch_n). Colocamos el código necesario para enseñaros claramente la opción nargs=»?»:
import argparse
analizador = argparse.ArgumentParser(description='Tutorial sobre argparse.')
analizador.add_argument(
"arch_orig",
help="Solicita un nombre de archivo de origen.",
)
analizador.add_argument(
"arch_dest",
nargs="?",
default='arch_dest.txt',
help="Solicita un nombre de archivo destino, si se omite utiliza 'arch_dest.txt'.",
)
argumento = analizador.parse_args()
print("Argumentos: archivo de origen y destino ('arch_det.txt' si se omite destino)")
print("Nombres de archivos:")
print(argumento.arch_orig)
print(argumento.arch_dest)
Y esta sería la salida:
$ python3 tutorial_argparser.py
usage: tutorial_argparser.py [-h] arch_orig [arch_dest]
tutorial_argparser.py: error: the following arguments are required: arch_orig
$ python3 tutorial_argparser.py lista.txt
Argumentos: archivo de origen y destino ('arch_det.txt' si se omite destino)
Nombres de archivos:
lista.txt
arch_dest.txt
$ python3 tutorial_argparser.py lista.txt lista_ordenada.txt
Argumentos: archivo de origen y destino ('arch_det.txt' si se omite destino)
Nombres de archivos:
lista.txt
lista_ordenada.txt
Como abreboca al estudio avanzado de argparse colocamos el siguiente ejemplo, muy sencillo pero que ilustra hasta donde podemos llegar combinando opciones:
import argparse
import os
analizador = argparse.ArgumentParser(description='Tutorial sobre argparse.')
analizador.add_argument(
"arch_orig",
help="Solicita un nombre de archivo de origen.",
)
analizador.add_argument(
"arch_dest",
nargs="?",
default= os.getcwd()+'/arch_dest.txt',
help="Solicita un nombre de archivo destino, si se omite utiliza 'arch_dest.txt'.",
)
En color verde resaltamos la añadidura: primero tenemos que importar la librería os. Uno de los métodos es os.getcwd() la cual devuelve la ruta donde está almacenado nuestro guion, ¡probad vosotros! Es la manera de aprender.
Argumento opcional convertido en obligatorio.
Volviendo a nuestro ejemplo del argumento «-a» o»–aviso» (¿recordáis arriba?) lo podemos convertir en obligatorio adicionando un parámetro a la declaración del argumento, lo resaltamos en color verde:
analizador.add_argument(
"-a",
"--aviso",
required=True,
help="Detecta y confirma nuestro primer argumento."
)
Atención: el parámetro required NO es compatible con nargs=»*» ni con nargs=»?».
A medida que avanzamos se torna compleja nuestra aplicación, nuestra recomendación es transcribir y ejecutar, experimentar cada una de las diferentes combinaciones y una vez las tengamos comprendidas y bajo control avanzamos al siguiente nivel más complejo aún.
Argumento opcional con valor por defecto.
Ahora veremos que un argumento opcional le podemos dar un valor por defecto y así lo invoquemos sin ningún tipo de argumento utilice dicho valor prefijado. Además, si el usuario desea introducir algún valor deberá colocar la palabra clave acompañada de un tipo de valor por nosotros especificado (texto, entero, etc.). En este punto nos vamos acercando a la manera de como normalmente se comportan las aplicaciones más comunes, es decir, un comportamiento bastante común; acá la codificación de ejemplo:
import argparse
analizador = argparse.ArgumentParser(".:|Tutorial sobre argparser|:.")
analizador.add_argument(
'--limite',
default=3,
type=int,
help="Especifique el número máximo de elementos, (por defecto 3).")
argumento = analizador.parse_args()
print("Límite: {}".format(argumento.limite))
Si
$ python3 tutorial_argparser.py
Límite: 3
$ python3 tutorial_argparser.py --help
usage: .:|Tutorial sobre argparser|:. [-h] [--limite LIMITE]
optional arguments:
-h, --help show this help message and exit
--limite LIMITE Especifique el número máximo de elementos, (por defecto 3). 3.
$ python3 tutorial_argparser.py --limite 17
Límite: 17
Argumento obligatorio y que exige escoger de una lista de opciones.
Muchas veces necesitamos que un usuario escoja un solo valor de una lista de opciones. Por ejemplo, solicitamos qyue escoja un mes de inicio de trimestre, el código sería el siguiente:
import argparse
analizador = argparse.ArgumentParser(description='Tutorial sobre argparse.')
analizador.add_argument(
"mes",
choices=['Enero','Abril','Julio','Octubre'],
help="Permite escoger un mes de comienzo de trimestre.",
)
argumento = analizador.parse_args()
print("Argumento: solicita un mes de una lista predeterminada.")
print("Mes escogido:")
print(argumento.mes)
Y cuando lo ejecutamos:
$ python3 tutorial_argparser.py
usage: tutorial_argparser.py [-h] {Enero,Abril,Julio,Octubre}
tutorial_argparser.py: error: the following arguments are required: mes
$ python3 tutorial_argparser.py Junio
usage: tutorial_argparser.py [-h] {Enero,Abril,Julio,Octubre}
tutorial_argparser.py: error: argument mes: invalid choice: 'Junio' (choose from 'Enero', 'Abril', 'Julio', 'Octubre')
$ python3 tutorial_argparser.py Julio
Argumento: solicita un mes de una lista predeterminada.
Mes escogido:
Julio
Como ven, ¡tremendamente útil!
Un breve receso antes de continuar con…
Get comfortable with abstraction. If you try to understand how everything works, you'll get nothing done. pic.twitter.com/jfsXtdIySk
Como ya estamos prácticos con argparse (o deberíamos, sino retroceded y repasad) vamos a abstraernos un poco. Imaginemos que poseemos una impresora 3D, es decir, una ‘impresora’ capaz de producir objetos físicos tangibles. Nuestro programa será capaz de ‘imprimir’ bien sea un cubo, bien sea una esfera pero no ambos al mismo tiempo. Para ello codificamos de la siguiente forma y manera:
import argparse
analizador = argparse.ArgumentParser(description='Tutorial sobre argparse.')
grupo = analizador.add_mutually_exclusive_group()
grupo.add_argument(
"-c",
"--cubo",
action = "store_true",
help="Imprime un cubo en tercera dimensión.",
)
grupo.add_argument(
"-e",
"--esfera",
action = "store_true",
help="Imprime una esfera en tercera dimensión.",
)
argumento = analizador.parse_args()
if argumento.cubo:
print("'Imprime' un cubo")
if argumento.esfera:
print("'Imprime' una esfera")
En este caso, como los parámetros son opcionales si no «pasamos» nada pues nada hace. Pero si empleamos –cubo o –esfera dará como resutlado lo correspondiente, pero si usamos las dos al mismo tiempo nos indicará que escojamos solo una de ellas.
Si sois avezados notando los detalles, veréis lo coloreado en verde: un parámetro nuevo llamado action. Por increíble que les parezca, en realidad ya lo estuvimos usando desde hace rato: lo empleamos para saber si un parámetro opcional ha sido «pasado» a la aplicación, nuestro primer ejemplo hace uso de ello. La diferencia estriba que en aquel ejemplo debíamos acompañar de una cadena de texto y en este caso solo nos interesa el parámetro en si. Es decir, lo que nos interesa es si especificaron cubo o esfera y que lo represente como una variable booleana. Para que almacene un valor verdadero le asignamos store_true y si es un valor falso pues store_false. Esto último es un poco liado ¿para qué diantres necesitamos un valor falso?
En el siglo IX los chinos inventaron la brújula (aguja imantada suspendida que siempre apunta al polo norte) y desde entonces le destinaron en cada barco un habitaculum (en latín, habitáculo en castellano) al cual los franceses le nombraron habitacle y que luego abreviaron como bitacle y que pasó a ser traducido al castellano como bitácora (a pesar de que ya teníamos la palabra traducida directamente del latín, habitáculo). Pues bien, se necesitaba llevar un registro de la posición del barco en los largos viajes por nuestro globo terráqueo (y junto al sextante para registrar los astros) todo se anotaba en un cuaderno de bitácora, o simplemente bitácora.
¿A donde nos lleva esta introducción que aparentemente no tiene nada que ver con computación? ¡Ya veremos!
Introducción.
Así como los gobiernos en tierra necesitaban conocer qué sucedió en un navío en altamar a su regreso, nosotros necesitamos saber qué sucedió en los programas que para bien desarrollemos para nuestros usuarios. Lo más básico es mostrar mensajes por pantalla a los usuarios y confiar en que ellos y ellas nos retribuyan debidamente la información… pero con muy contadas excepciones, podemos esperar sentados para no cansarnos porque eso será difícil que se haga realidad.
Es por ello que debemos guardar un registro metódico para que posteriormente podamos evaluar qué funcionó mal (por extraño que parezca, si funciona bien pues felices de la vida aunque no recibamos las felicitaciones de nuestros usuarios y usuarias de software). Otra razón de llevar un registro sería la de análisis de desempeño o incluso ejecutar un programa en modo de depuración.
La razón y la lógica indica que dichos registros que pensamos llevar deberían ser guardados en una base de datos pero en proyectos pequeños tal vez no necesitemos tal nivel de complejidad. Pongamos por caso el programa Filezilla que tiene ambas versiones tanto como servidor como cliente: por defecto no se registra mensaje alguno a menos que así lo deseemos y si decidimos guardarlo podemos especificar un archivo llevando la fecha de cada evento (opcional) e incluso podemos limitar a un tamaño específico tras lo cual al alcanzar dicho valor se procede a crear un archivo nuevo pero sin la extensión «.log» la cual es sustituida por una numeración consecutiva.
Por esta y muchas otras razones el lenguaje Python 3 tiene disponible una librería destinada para tal efecto, estudiemos pues.
Creando una aplicación modelo.
Antes de crear siquiera registro alguno debemos tener, claro está, un software al cual llevarle un registro. Para ello proponemos un programa que llamaremos calculadora1.py cuyo código es el siguiente (si queréis repasar vuestro conocimientos básicos sobre Python, revisad nuestro tutorial al respecto):
El código es bastante sencillo, solo las cuatro operaciones aritméticas básicas: suma, resta, multiplicación y división; reconocemos que el código es un tanto extraño pero recordad que tiene propósitos didácticos solamente. Creamos una clase con funciones que no emplean return sino que muestran por pantalla los resultados excepto en la inicialización que muestra un mensaje puramente informativo, emulando el «on» de una calculadora electrónica y anunciando el modelo virtual. Abstraigámonos entonces en el ejemplo para comenzar a modificarlo con el registro de eventos.
Agregando la utilería «logging».
Para comenzar a utilizar la librería logging debemos incorporarla a nuestro archivo con el siguiente código:
La primera línea enlaza la librería y la segunda línea configuramos con una constante logging.DEBUG (que tiene un valor decimal de diez), osea, el nivel («level«) que vamos a usar: modo de depuración.
Notad todos y todas que en GNU/Linux son distintas las mayúsculas de las minúsculas, por lo tanto logging.DEBUG es una constante y logging.debug es un método, diferenciad bien esto en el siguiente código que modificamos a partir de la aplicación modelo.
Como vemos en la siguiente imagen la salida por pantalla ha sido modificada ya que le agrega «DEBUG:root» a todos los mensajes de resultado.
python3 calculadora2.py
La primera palabra indica que estamos en modo de registro a nivel de depuración «DEBUG» y la segunda palabra indica que estamos depurando el módulo principal aunque esto no es realmente cierto. Lo mejor sería indicar desde dónde estamos imprimiendo el mensaje de depuración, en nuestro caso cualquiera de las cuatro funciones. Para ello vamos a volver a modificar el programa -que ya hemos renombrado como calculadora2.py– especificando cada función por separado:
Bien, pues ya estamos listos para comenzar a grabar en un archivo de texto plano nuestros eventos. Esto se logra configurando de nuevo el encabezado logging.basicConfig el cual ahora lo ocuparemos en varias líneas para buscar una mayor claridad para cada uno de sus parámetros:
Por supuesto el archivo será guardado en la misma carpeta donde se ejecuta la aplicación y para nuestra sorpresa al ejecutarla ya no muestra nada por pantalla… lo cual no es lo que realmente queremos hacer pero paciencia, primero analizemos el archivo resultante.
cat bitacora.log y combinado con el comando grep
Al usar el comando cat podremos, entre otras cosas, listar el contenido de un archivo por pantalla y como probablemente la cantidad de mensajes generará gran cantidad de líneas podremos filtrar los resultados por palabra clave. ¿Recordáis que dimos nombres diferentes para la muestra de resultados a nivel de cada función? Pues con el comando grep que recibe el resultado del comando cat por medio del comando «tubería» «|» y la palabra clave «Sum» o «Mul» podremos ver lo que nos interese. Ya nuestra aplicación está entrando en modo pragmático, ¡lo realmente útil para nosotros!
Agregando más pragmatismo aún: claridad al registro.
Nosotros los seres humanos en nuestro cerebro siempre buscamos darle «orden» a nuestro mundo, así está torcido lo tratamos de ver derecho y esto en el registro de eventos no ha de ser la excepción. Ya le colocamos para saber cual función produce tal registro pero le agregaremos mayor claridad en el apartado de configuración al inicio de la aplicación:
Recordad siempre al final de cada línea colocar una coma para separar los parámetros, que como es multilínea tendemos a pensar que cada retorno de carro automáticamente separa cada parámeto pero no es así.En el tercer parámetro mandamos a separar con par de espacios y un guion las diferentes secciones de cada evento en cada línea:
Fecha y hora exacta hasta en milisegundos cuando ocurrió el evento.
Nombre del módulo donde se origina cada evento, en nuestro caso cada función.
Nivel del mensaje, clasificación (hasta ahora estamos en modo de depuración solamente DEBUG).
El mensaje en sí mismo.
cat bitacora.log
Formato de tiempo mejorado.
Al formato de estampado de fecha y hora lo podremos mejorar agregando otra línea más al encabezado de configuración con una máscara que también es utlizada por el comando time.strftime():
Dejamos para vosotros os ejercitéis y veáis cómo es distinto los nuevos registros que se siguen adicionando de manera automática al final de nuestro archivo destinado a tal efecto, bitacora.log
Nivel de registro de eventos.
Como hemos repetido varias veces, del modo de depuración DEBUG no nos hemos movido hasta ahora. Por ello debemos estudiar los diferentes niveles -y constantes- que utiliza la librería logging: ya sabemos que logging.DEBUG vale diez -y se van incrementando de diez en diez- pero he aquí la tabla completa de valores:
Nivel
Valor
numérico
Función
Uso
NOSET
0
no aplica
no aplica
DEBUG
10
logging.debug()
Diganóstico de problemas, muestra información bien detallada.
INFO
20
logging.info()
Confirma que todo está funcionando correctamente.
WARNING
30
logging.warning()
Indica que algo inesperado ha sucedido, o pudiera suceder.
ERROR
40
logging.error()
Indica un problema más serio.
CRITICAL
50
logging.critical()
Muestra un error muy serio, el programa tal vez no pueda continuar.
Agregando mensajes de error y su registro.
A nuestra aplicación vamos a agregarle un mensaje de error en la función de división, bien sabemos que cualquier número dividido entre cero tiende al infinito el cual es un concepto que entedemos los seres humanos pero los ordenadores no. La modificación es la siguiente (notad que b valdría uno si el valor no es pasado a la función para tratar de evitar este error):
def dividir(self, a=0, b=1):
if (b==0):
bita_div.error("Alerta: el divisor debe ser distinto a cero.")
else:
bita_div.debug("División a={} b={} a/b={}".format(a,b,a/b))
También modificamos el divisor en la función de división a calc.dividir(36,0) y el resultado en el registro de errores mostraría algo aprecido a esto:
Calculadora modelo A1 (encendida).
03/05/2017 11:03:07 PM - Sum - DEBUG - Suma a=36 b=3 a+b=39
03/05/2017 11:03:07 PM - Res - DEBUG - Resta a=36 b=3 a-b=33
03/05/2017 11:03:07 PM - Mul - DEBUG - Multiplicación a=36 b=3 a*b=108
03/05/2017 11:03:07 PM - Div - ERROR - Alerta: el divisor debe ser distinto a cero.
Lo próximo que haremos es modificar de manera completa nuestra aplicación con los diferentes «niveles» de mensajes.
Empleando diferentes niveles de registro.
Volvamos nuestros pasos sobre la sección logging.basicConfig donde contiene el nivel de registro de eventos para nuestra aplicación. Recordemos que la establecimos a nivel DEBUG y ahora la estableceremos a nivel INFO, guardaremos y ejecutaremos de nuevo la aplicación. Luego revisaremos el fichero bitacora.log y notaremos que no se registró el mensaje de inicialización pero si quedaron registrados los mensaje de información (y por supuesto el mensaje de error).
El siguiente paso es elevar al nivel de WARNING para obtener solamente el mensaje de error por la división entre cero y se repite el resultado si lo elevamos a nivel ERROR. No obtenderemos mensaje alguno si lo establecemos a nivel CRITICALya que la divisón entre cero no solamente ha sido debidamente advertida sino que también ha sido debidamente desviada.
Pro último, y más difícil de obtener (según la aplicación de modeo didáctico que de exprofeso escogimos) es el mensaje a nivel CRITICAL. Volvemos a repetir, este comportamiento es circunstrito estrictamente a nuestra aplicación modelo: la división está en el mensaje mismo a mostrar en bita_div.CRITICAL y nunca lograremos que se muestre ya que está debidamente desviado además, si no lo desviaramos al ejecutar el compilador Python3 inmediatamente nos mostraría el error si intentamos dividir entre cero y por ende no se ejecuta el programa.
Nosotros somos de experimentar al máximo, nos hacemos muchas, muchísimas preguntas: ¿Y si compilamos la aplicación, es decir la convertimos a lenguaje binario para ejecutar y lograr el mensaje a nivel CRITICAL?
Para ello -brevemente- podemos instalar PyInstaller:
sudo pip3 pyinstaller
Luego simplemente vamos a la carpeta con nuestro fichero calculadora2.py (habiendo eliminado la desviación del error de división entre cero):
pyinstaller calculadora2.py
Y luego de cierto tiempo (¡oh, sorpresa, también utiliza logging para mostrar el progreso de la compilación pero con unos códigos no recomendables de niveles de registro -valores personalizados-) y en una carpeta dist encontraremos nuestro ejecutable listo para ser experimentado. Nosotros obtuvimos esto, si queréis practicad que algo parecido obtendréis:
pyinstaller calculadora.py
Entonces estaremos listo para ejecutar nuestro flamante binario, nos vamos con el explorador de ficheros Nautilus de Ubuntu a la carpeta dist le damos click derecho, ejecutar y ¡oh, sorpresa! el fichero bitacora.log se genera y aparece… pero con cero bytes, sin nada dentro, ¿qué ha sucedido aquí?
bitacora.log vacio cero bytes
Pues que simplemente la librería de registro abre el archivo bitacora.log (crea el archivo) pero la división entre cero no le permite llegar a ejecutar el grabado del mensaje, ya que las instrucciones son anidadas y primero trata de dividir y luego mostrar el mensaje, pero como se «cuelga» pues no registra nada de nada.
En este punto ya es bueno concluir algo muy cierto: el registro de errores incluso nos beneficiará al obligarnos a pensar dónde colocar los mensajes necesarios para futuras mejoras y en el caso del software libre donde TODOS podemos ser parte de un equipo de programadores esta ayuda es tremendamente bienvenida.
Otra pregunta que nos hacemos, ¿qué sucede si no establecemos un nivel de registro específico en logging.basicConfig? De manera predeterminada la utilería está en nivel WARNING y los mensajes que sean iguales o superiores a este nivel serán registrados (WARNING, ERROR y CRITICAL). No obstante vamos a dar un paso más allá en nuestros estudios y vamos a configurar para que sean nuestros propios usuarios quienes establezcan un nivel de registro lo cual consideramos útil para ellos que NO son programadores y que tal vez necesiten cierta orientación sin necesidad que ellos y/o ellas lleguen a tener que descargar el código fuente de la aplicación -que siempre estará al alcance por ser software libre-.
Que los usuarios y usuarias establezcan su nivel de registro.
Pasando parámetros a una aplicación desde la línea de comando.
En el mundo de Python hay varias librerías que nos permiten «pasar» parámetros hacia «lo interno» de nuestras aplicaciones, algunas de ellas son -pero no son todas-:
getoptes una librería la cual se deriva de una del lenguaje C llamada, claro, getopt().
optparseescrita para Python pero que actualemente está «descontinuada».
argparse la cual curiosamente deriva de optaprse pero ofrece total compatibilidad a la versión 3 -y a futuro-.
Por esa razón escogemos esta última para evitarnos dolores de cabeza a futuro.
argparse.
Introducción a argparse.
Debemos hacer una breve introducción al concepto de parámetros tanto opcionales como obligatorios. De manera general las aplicaciones corren sin ninguna isntrucción especial: escribimos el nombre del fichero, el sistema operativo revisa si es un ejecutable, o carga en memoria y ejecuta las instrucciones contenidas.
Un ejemplo sencillo es el comando para listar ficheros y directorios en una ventana terminal: ls. Sin más dicho comando nos muestra por pantalla los ficheros y directorios contenidos en la carpeta desde donde la ejecutamos. Si hubiera alguna carpeta y queremos saber su contenido debemos escribir ls nombre-de-la-carpeta y allí tenemos un parámetro opcional que le estamos pasando a la aplicación: le estamos ordenando listar el contenido de un directorio. Decimos que es opcional porque, como vimos, el comando no necesita nada para funcionar pero somos nosotros los que tenemos la necesidad de pedirle algo muy específico. Pero adicionalmente a la petición específica queremos que nos lo muestre de una manera específica y para poder diferenciar los nombres de las carpetas -o archivos- que pidamos de la forma como la va a presentar pues nació la idea de colocar palabras claves para diferenciar (recalcamos que estamos con el comando ls como ejemplo útil ya que es un comando extremadamente básico). Así podemos teclear ls nombre-de-carpeta -l para listar en modo columna o el también llmado modo largo (nombres de ficheros o directorios uno encima del otro con detalles de tamaños, fecha, atributos, etc.).
Es por esta razón que se estableció ciertas normas para pasar parámetros, en general podemos decir que:
Se utiliza un guion «-» como prefijo para indicar un parámetro y se acompaña generalmente con una sola letra que más que suficiente porque tenemos 54 opciones distintas (27 caracteres mayúsculas y minúsculas).
Como estrategia nemotécnica se utilizan dos guiones juntos «–» junto con palabras o incluso frases para que sea de manera explícita su recordación.
También se da el caso que a las dos opciones anteriores se le agregue sin dejar espacios un signo de igualdad y a continuación algún valor condicionante (que puede ser imprescindible o no).
Como para algunos el punto anterior no les parece elegante, también se estila colocar un espacio y a continuación algún valor condicionante.
Siguiendo con el ejemplo del comando ls:
Ejemplo del punto 1: comando «ls -r» (lista los archivos y carpetas en orden alfabético inverso, de la letra zeta hacia la letra a).
Ejemplo del punto 2: comando «ls –reverse» ídem al punto anterior pero más fácil de recordar y explícito para mostrar.
Ejemplo del punto 3: comando «ls –sort» ordena las lista de ficheros por orden de tamaño, del más grande hacia el más pequeño, pero sucede que hay muchas maneras de ordenar ese listado y si lo ejecutamos así sin más nos solicita un parámetro necesario. Podemos pedirlo por tamaño así que escribimos «ls –sort=size» y veremos el resultado con primero los más grandes yendo luego progresivamente hasta los más pequeños. Por cierto, este comando «largo» tiene un equivalente «corto»: ls -s.
Ejemplo del punto 4: comando «ls patron-a-buscar» como por ejemplo si queremos ver solamente los archivos que comienzen con la letra «a»: «ls a*«.
Primeros pasos con argparse.
Para comenzar a utilizar argparse en nuestro programa, simplemente hagamos un fichero nuevo y le colocamos los siguiente:
import argparse
analizador = argparse.ArgumentParser(description="Programa demostrativo de argparse")
analizador.parse_args()
Al salir lo nombramos como mi_programa.py y lo ejecutamos con python3 mi_programa.py y, por supuesto, no hace nada de nada ya que no le escribimos ningún código adicional. Pero ahora vamos a ejectuarlo acompañado de un parámetro como lo es el siguiente:python3 mi_programa.py -h y obtendremos la siguiente salida:
usage: mi_programa.py [-h]
Programa demostrativo de argparse.
optional arguments:
-h, --help show this help message and exit
Como vemos todo viene preconfigurado para utilizar el idioma inglés por defecto, pero pronto podremos darle un uso mejor para orientarlo hacia el idioma castellano en un tutorial dedicado al tema. Notad que especifica que el «parámetro largo» para ayuda es –help. Adicionalmente, cualquier otro parámetro que le pasemos manifestará desconocerlo -no hemos programado nada áun, por ahora-. Por lo pronto ya cumplimos con iniciar el uso práctico de argparser, continuemos aprendiendo.
Agregando otro argumento opcional.
Como vemos argparse tiene al menos un argumento establecido por defecto, el de ayuda [el cual es opcional, está mostrado entre corchetes], y ahora nosotros vamos a agregarle nuestro propio argumento para establecer el nivel de registro de eventos. Para ello especificaremos la palabra clave -log_lev acompañado del nivel que deseemos establecer, a continuación lo pasamos por una serie de tamices con la instrucción condicional if~elif~else y si coincide mostramos por pantalla la opción elegida:
import argparse
analizador = argparse.ArgumentParser("Programa demostrativo de argparse")
analizador.add_argument("-log_lev", help="Utilice DEBUG, INFO, WARNING, ERROR o$
analizador.parse_args()
argumentos = analizador.parse_args()
if argumentos.log_lev == 'DEBUG':
print("DEBUG")
elif argumentos.log_lev == 'INFO':
print("INFO")
elif argumentos.log_lev == 'WARNING':
print("WARNING")
elif argumentos.log_lev == 'ERROR':
print("ERROR")
elif argumentos.log_lev == 'CRITICAL':
print("CRITICAL")
else:
print("Opcion no válida de nivel de registro de eventos.")
Como vemos esto simplemente es el armazón para el manejo del pase de parámetros a nuestro programa didáctico para el registro de eventos con la utilería logging.
En este punto corred vuestro programa varias veces, experimentad con el pase de parámetros para que luego continuemos con el último paso de este tutorial: la fusión de logging con argparse.
Uniendo «logging» con «argparse».
Ya para finalizar unimos el código de ambos ejemplos y la idea es la siguiente: al utilizarse sin parámetros se establece el nivel de registro en WARNING que es el nivel predeterminado. Si se utiliza el parámetro -log_lev sin acompañarlo de valor alguno, la librería argparse se encargará debidamente de orientar al usuario sobre las opciones disponibles. Si el usuario usa alguna opción disponible válido pues se establece debidamente el nivel de registro correspondiente.
Queda para vuestra práctica el permitir que los usuarios especifiquen un nombre de archivo para el registro de eventos.
Acá tenemos el código final, espero os haya servido para aprender algo nuevo sobre el lenguaje Python.
La verdad es que revisando los temas publicados por nuestros colegas de GNULinuxBlog (por ahora quien publica el tema, el sr. Elías Rodríguez Martín y el sr. José Miguel, creador del blog) encontramos uno en particular muy útil y que nosotros habíamos enfocado de una manera un tanto complicada en comparación con la facilidad de lo que allí proponen. Cuando conseguimos que alguien es más listo que nosotros inmediatamente lo reconocemos y aprendemos de dichas personas, no tenemos rubor en admitirlo, por eso os pedimos que nos acompañéis en nuestro artículo de hoy: compartir ficheros de una manera rápida y sencilla con Python.
La Norma de Diseño de Cuadrícula que próximamente estará disponible en los navegadores web nos permite diseñar una serie de elementos por medio del Diseño de Hojas en Cascada (Cascade Style Sheets o mejor conocido por su acrónimo de tres letras CSS) con una rapidez asombrosa y pocas líneas de código. Por cierto, la imagen de introducción a este artículo fue creada por medio de código, no utilizamos el ratón ni una sola vez.
En nuestra entrega anterior -y con la ayuda del Doctor Jason Brownlee- instalamos Fedora 25 como máquina virtual para tener un entorno de desarrollo específico para Python3: pues bien, el fin último es tener una base para poder desarrollar el «machine learning» a un nivel intermedio, veamos.
¿Qué es el «machine learning»?
Es una ciencia aplicada con ayuda de los ordenadores: de una serie gigantesca de datos obtenidos de nuestro mundo analógico regido por la mecánica cuántica podemos obtener patrones, precisamente lo que hacemos nosotros los seres humanos, como por ejemplo cuando miramos una noche estrellada y percibimos constelaciones que solo están en nuestro cerebro. Muchas universidades en el mundo están abocadas a este estudio y promete ser el futuro en el cual debemos estar preparados para cuando lleguen los ordenadores cuánticos.
Un dato que nos llama la atención es que ya hoy en día tiene una aplicación práctica, para mal, denunciada por Edward Snowden en su cuenta en Twitter:
¿Qué tiene que ver esto con el «machine learning»? Pues que el dispositivo que es utilizado para detectar los patrones de atención de los televidentes durante los comerciales es grabado con un dispositivo originalmente diseñado para juegos denominado Kinetic. En realidad es un dispositivo biométrico que en el año 2011 incluso ganó un premio de 50 mil libras esterlinas por desarrollo innovador al reconocer los movimientos de nuestro cuerpo, interpretarlos y trasladarlo como acciones en los juegos de vídeo. Es así que en solo seis años se le ha conseguido un uso importante en la vida real, acercándonos cada vez más a las historias contadas en las saga de películas «Terminator».
Instalando un entorno basado en Python para el aprendizaje de las máquinas.
Instalando el entorno Python.
Como ya bien sabéis, ya sea por este vuestro humilde portal o por internet -que abunda información- el lenguaje de programación Python viene instalado por defecto en la mayoría de las distribuciones GNU/Linux y Fedora no es la excepción. Para saber cual versión tenemos instalada, ejecutamos nuestra máquina virtual e introducimos nuestras credenciales de usuario. Acto seguido pulsamos ALT+F2 e introducimos la orden «gnome-terminal» para ejecutar una ventana de comandos para lanzar la siguiente línea:
python3 --version
En la figura podemos observar que ya tenemos instalada la versión 3.5.2:
python3 –version
Instalando el conjunto de utilidades para Python3.
Los paquetes que necesitaremos son seis en total:
SciPy, conjunto de herramientas que integra los siguientes paquetes:
NumPy: herramienta para álgebra lineal, transformadas de Fourier y generación de números aleatorios.
Todas ellas las podemos bien instalar en una sola línea, bien una por una, depende de nuestra paciencia. Por rapidez escogeremos la primera y en la misma ventana terminal que tenemos abierta colocamos:
El comando DNF es la abreviatura para Dandified Yum, que tal como explicamos, es el equivalente al comando apt-get utilizado en las distribuciones GNU/Linux basadas en Debian. Así que autorizamos la descarga, que son más o menos 92 megabytes:
Una vez se han descargado a nuestro ordenador virtual, se hace la verificación de contenido y se procede a descomprimir e instalar; allí van 200 megabytes adicionales:
Instalando entorno Python3 para «machine learning» 3Instalando entorno Python3 para «machine learning» 4
Confirmación del entorno instalado.
Una vez finalizado procedemos a «crear» un guión en bash para averiguar rápidamente las versiones instaladas. Aquí viene la utilidad de haber instalado las utilerías de «VirtualBox Guest Additions» en nuestra entrada anterior: podremos «copiar y pegar» texto entre nuestro ordenador anfitrión y el ordenador virtual.
Habilitar el portapapeles entre VirtualBox y las máquinas virtuales.
Antes de poder «copia y pegar» texto debemos primero habilitar su paso por medio de VirtualBox: seleccionamos la máquina virtual Fedora y presionamos CONTROL+S para entrar a la configuración y vamos a la sección «Interfaz de usuario» -> «Dispositivos»:
VirtualBox portapapeles entre ordenador anfitrión y ordenador virtual
Descolgamos la lista y seleccionamos «Anfitrión a invitado» y damos «Aceptar».
VirtualBox portapapeles ordenador anfitrión a invitado
Archivo de comando «versions.py».
Ahora que podemos pasar el portapapeles a la máquina virtual (tedioso procedimiento para poder habilitarlo), seleccionamos, copiamos y pegamos lo siguiente:
En las siguientes imágenes utilizamos el editor de textos gedit, pero sentiros libres de utilizar vuestro preferido (de hecho nosotros más adelante instalamos nano):
Guión para detectar los componentes instalados de Python3 unoGuión para detectar los componentes instalados de Python3 dosGuión para detectar los componentes instalados de Python3 tresGuión para detectar los componentes instalados de Python3 cuatroGuión para detectar los componentes instalados de Python3 cinco
Comenzamos el 2017 con un breve tutorial sobre Etherpad, específicamente la versión ligera o Etherpad Lite, la cual es un editor de texto colaborativo en línea sin mayores pretensiones de seguridad y/o registro pero sencilla y poderosa en concepto. Veremos como instalarla, utilizarla y hasta revisaremos de forma sencilla cómo funciona, acompañenos por favor 😎 .
Introducción.
Mucho se habla de las «nubes» en forma metafórica y siempre hemos sido un tanto excépticos al uso -y abuso- de dicho término. Somos sobre todo pragmáticos pero también cautelosos en cuanto a la tecnología que almacena nuestros datos en internet y «prometen el oro y el moro» a diestra y siniestra pero he aquí que nos llamó poderosamente la atención la herramienta Etherpad por su aparente sencillez -luego veremos muy bien los detalles «de debajo del capó«-.
En los últimos tiempos hemos visto el surgimiento de aplicaciones en línea (documentos de texto, hojas de cálculo y hasta edición de imágenes) que por una tarifa mensual alquilamos el almacenamiento de los ficheros –de algo tenemos que vivir los programadores, comemos y tenemos familias que mantener- pero tal vez no necesitemos de una herramienta tan sofisticada para nuestros propósitos. He aquí que interviene Etherpad: podremos tener «listas por hacer», documentos, declaraciones, ENTREVISTAS en línea con resaltado sobre lo que escribe cada participante identificado con diferentes colores y podremos embutirlo en cualquiera de nuestras aplicaciones con tan solo incluir una ventana al navegador web que gustéis (lo probamos con Mozilla Firefox y Chromium). Esencialmente eso es lo que hace Etherpad, después que lo instalemos ampliaremos en mayores detalles, no muchos debido a la aparente simplicidad de la aplicación.
La historia de Etherpad está bien resumida en el siguiente párrafo: la aplicación fue lanzada el 19 de noviembre de 2008 por David Greenspan, Aaron IBA y J. D. Zamfirescu (los dos últimos solía trabajar para Google). Más tarde se les unieron -los también empleados por Google- Daniel Clemens y el diseñador David Cole. El sitio original fue llamado etherpad.com (hoy en día el enlace redirige hacia etherpad.org). El 4 de diciembre de 2009 Google compró AppJet, la compañía que posee el software y se anuncia que esta solicitud de colaboración estará disponible bajo licencia Open Source. El 17 de diciembre de 2009, Google libera los código fuentes de Etherpad bajo licencia Apache v2.
Etherpad Lite fue reescrita completamente en un servidor JavaScript utilizando Node.js y del lado del cliente también utiliza javaSript en un 99%.
Los mismos programadores, que antes trabajaron para Google, destacan por Twitter la versatilidad de Etherpad y se erigen como competencia directa al servicio «Google Docs»:
La última versión también está alojada en GitHub.com y está amparada por la licencia Apache 2.
etherpad lite animated.gif
Ambiente de trabajo.
Nosotros utilizamos un ordenador con Ubuntu 16.04 en una red de área local que configuramos por DHCP que nos otorga la siguiente dirección IPv4 fija: 192.168.1.47, aparte de tener derechos de administrador «root» eso es todo lo que necesitamos, ¿sencillo, cierto?
Cada una de las palabras a la derecha de «apt install» respresenta un sfotware en particular que no describiremos detalladamente pero por acá hemos escrito sobre Git y Python, por si acaso queréis ampliar vuestros conocimientos pero para el caso que nos ocupa no es necesario saber usarlos -pero al menos tener noción de que existen-.
Como véis nosotros ya teníamos instalado lo necesario debido a nuestro trabajo de programación en otros asuntos (esto es lo que denominamos entorno de trabajo, se comparten muchos elementos, cuidad de poner muchos proyectos en una sola computadora por razones de estabilidad y uso de versiones compartidas).
En la imagen anterior, por error, no usamos la orden «sudo mv» y eso ocasionó los mensajes de «no se pude mover» es por ello que casi no usamos el concatenador «&&«, generalmente ejecutamos paso por paso, pero bueno, errar es de humanos y reconocemos nuestro fallo, a la final completamos el proceso en una segunda línea.
Tras la pequeña falla subsanada, agregamos los enlaces simbólicos hacia nuestro perfil de usuario en el ordenador:
echo "PATH=$PATH:/opt/nodejs/bin" >> ~/.profile
Si todo sale bien pues felicitaciones, tenemos instalado Node.js en nuestra máquina de pruebas.
Instalando el código fuente de Etherpad.
En idioma inglés a esto lo llaman «instalar los (archivos) binarios», nosotros simplistas siempre pues lo llamamos código fuente o más simple aún: fuente de Etherpad. Para ello haremos lo siguiente:
Crearemos un directorio llamado etherpad en la carpeta opt.
Cambiaremos el propietario de la carpeta con chown hacia nuestro nombre de usuario whoami aprovechando las cualidades del bash y el uso de variables de paso entre comandos.
Cambiamos al directorio creado ya sin credenciales de administrador «root» porque la carpeta nos pertence ahora.
utilizamos el comando git para descargar el código fuente de Etherpad.
Para comenzar a usar Etherpad («levantar el servidor») pues ejjecutamos un archivo de procesos por lote o bash llamado «run.js» desde donde se verifican las variables de entorno (esencialmente nos prohíbe ejecutarlo con derechos de administrador «root» recordad que es un servicio web expuesto a la internet y a todo el mundo) y allana el camino para ejecutar a su vez a otro bash, installDeps.sh, que hace las siguientes comprobaciones:
Se asegura que NO se haya llamado desde el directorio bin (en nuestro ordenador de ejemplo nos quedó instalado en la siguiente vía: «/opt/etherpad/etherpad-lite»).
Comenzamos a usar el comando hash que devuelve cero si encuentra la vía donde están instaladas las respectivas aplicaciones (¿recuerdan lo que comentamos sobre agregar enlaces simbólico a la vía PATH al fichero /.profile)
En el caso que estemos ejecutando sobre SunOS necesitaremos instalar el comando ggrep -no se aplica a nuestro ordenador con Ubuntu-.
Verifica que tengamos instalado curl el cual usaremos para descargar lo que necesitemos de la internet.
Verifica si tenemos node.js instalado.
Verifica si tenemos npm instalado -y su versión-. Npm nos permite manejar cientos de miles de aplicaciones escritas para JavaScript.
Verifica la versión de node.js instalada (0.10.x o superior).
Confirma el nombre del archivo «settings.json» (pronto lo abriremos y editaremos).
De no conseguir «settings.json» pues copia una plantilla por defecto para poder comenzar a trabajar.
De nuevo tomamos a npm y le indicamos que actualize o instale toda nuestra «paquetería» (este comando puede tomar algo de tiempo dependiendo de vuestra máquina y vuestra velocidad de conexión al internet).
Verificamos que la versión jQuery que tengamos sea mínimo 1.9.1 de no ser así utilizamos curl para descargarla.
Importante: en la carpeta «/var» donde tengamos instalado Etherpad (para nuestro ejemplo en «/opt/etherpad/etherpad-lite/var») reposan los archivos que comienzan con la palabra «minified» que esencialmente contienen los guiones JavaScript que corren del lado del cliente. SUPONEMOS que son borrados y creados de nuevo cada vez que se ejecuta Etherpad porque se trata de reunir todas las piezas y entregarlas al cliente para que se ejecuten allá. Cada archivo contiene una licencia de uso Apache 2 e imploran ayudar a comentar -y ayudar- a mantener el código. Esta parte garantiza que cada que vez que «reiniciemos» a Etherpad sean instaladas y «compiladas» las intrucciones al cleinte con las nuevas versiones. También en esta carpeta reposa la base de datos «dirty.db» -manejada con SQLite- la cual es para propósitos de desarrollo y pruebas (lo que enseñamos nosotros en esta vuestra página web) pero que para entornos «de producción» recomiendan sustituir con mySQL.
Se asegura que tenga los guiones .js y .css «limpiecitos» antes de comenzar a ejecutar.
Nos tomamos el «atrevimiento» de copiar el archivo installDeps.sh tal cual se muestra al día sábado 07 de enero de 2017 y le colocamos, como valor agregado, instrucciones al usuario en idioma castellano. Si vosotros queréis contribuir al código fuente central cread una cuenta en GitHub y haced una divergencia de código «fork» en vuestra cuenta, programáis y modificaís y hasta quien sabe, a futuro podéis proponer cambios a los programadores originales para que lo incorporen a la rama oficial. Pero por ahora nosotros lo que queremos es enseñaros unos pocos archivos y no vale la pena copiar todo el código, por eso GitHub tiene una sección llamada Gist (tiza) para estos casos menores y rápidos, especial para ejemplos pequeños, mirad como queda nuestro trabajo:
Ahora si que se ejecuta el guión server.js el cual es el que consideramos «compila» y «levanta» el servidor pero mejor os colocamos la cabezera del archivo para que tengaáis una noción al respecto:
#!/usr/bin/env node
/**
* This module is started with bin/run.sh. It sets up a Express HTTP and a Socket.IO Server.
* Static file Requests are answered directly from this module, Socket.IO messages are passed
* to MessageHandler and minfied requests are passed to minified.
*/
/*
* 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
etherpad-lite bin run.sh (1)etherpad-lite bin run.sh (2)
Como vemos, debe ser llamado desde run.sh quien a su vez -ya explicamos- actualiza y borra datos anteriores con installDeps.sh y ejecuta el servicio en sí. Ahora tomad un cafecito y volvéis para mostraros cómo funciona del lado del cliente, con lo que nos toparemos todos los días.
Ejecutando del lado del cliente.
Nuestro servidor de pruebas está ubicado en la dirección IPv4 fija en nuestra red de área local con los números 192.168.1.47 y nos conectaremos con el puerto 9001 el cual es por defecto (de hecho ya tenemos un servidor web Apache2 escuchando en los puertos 80 y 443). Veremos lo siguiente:
Usando Etherpad-lite en servidor local (1)
Por otra parte nos fijamos en la ventana terminal desde donde lanzamos Etherpad y observamos cierta actividad.
Usando Etherpad-lite en servidor local (2)
En esta última imagen apreciamos la versión que tenemos instalada y la advertencia que no tenemos configurados al usuario «admin» y su contraseña (más adelante lo modificaremos) y también vemos que se ha creado un archivo llamado «prueba» con un nombre aleatorio y la dirección IP del navegador que tenemos conectado.
Esto es lo que veremos, un mensaje de bienvenida, en inglés (ya vamos a cambiar la configuración) y podemos desde ya el comenzar a escribir lo que necesitemos o queramos.
Usando Etherpad-lite en servidor local (3)
Desde otra computadora ubicada en la misma red de área lcoal nos conectamos y de una vez en el registro o bitácora nos mostrará a cual archivo se conectó y su dirección IP así como la dirección IP.
Usando Etherpad-lite en servidor local (4)
En ambas computadoras procedemos a identificarnos para que podamos visualizar en diferentes colores lo escrito por cada uno de los participantes.
Usando Etherpad-lite en servidor local (5)
Abajo a la derecha (esquina inferior derecha) hallaremos el ícono para iniciar una conversación aparte del codumento que estemos editando. No consideramos explicar mayor cosa porque sabemos que los usuarios son expertos en esto de los «chats» 😉
Usando Etherpad-lite en servidor local (6)
Si hacemos click en la esquina superior derecha, en el ícono de una rueda dentada, abriremos las opciones de configuración para que nos sintamos a gusto en nuestro entorno:
Mostrar siempre la conversación en pantalla.
Mostrar conversación y usuarios.
Ver lo que escribe cada usuario con su respectivo color.
Mostrar los números de línea (útil si un documento es extenso y queremos mostrar específicamente algo a otros usuarios ).
Podemos elegir si queremos escribir de derecha a izquierda (idiomas como el árabe, ver siguiente punto).
El menú desplegable para cambiar el idioma, hay muchos, incluido el «español» -castellano en realidad-.
Configurando Etherpad
Configurando Etherpad para su uso del lado del cliente.
Imaginemos ahora que ya tenemos «montado» nuestro servidor en producción y queremos compartir el enlace al documento que estamos editando, pues hacemos click en el ícono «</>» y copiamos el enlace para enviarlo por correo electrónico, Twitter, etc.
Compartiendo enlaces en Etherpad
Visualizando los cambios realizados.
Muy útil poder visualizar los cambios realizados, Etherpad conserva un historial extremadamente detallado, lo notamos mucho cuando se importan documentos al trabajo que estamos realizando (ver el próximo punto). Para ello hacemos click en el ícono de reloj de manecillas y podremos adelantar, atrasar y cada cambio hecho se mostrará en pantalla.
Visualizando cambios en un documento de Etherpad
Importando documentos.
Esta opción no es muy lógica que digamos pero bueno, muchas veces podemos ahorrar tiempo «copiando y pegando» o, como en este apartado, importando documentos. Las instrucciones dicen que deben ser en formato .html .txt o formato Etherpad pero si instalamos AbiWord podremos «soportar» más formatos de archivo. Para este proceso damos click en las flechas encontradas y «subimos» el documento deseado.
Importando ficheros a un documento Etherpad
Guardando documentos.
Con solo hacer click en la estrella de cinco puntas el documento será guardado en la base de datos «dirty.db» para una próxima sesión (recordad que podemos configurar para que trabaje con MySQL). Si queremos llevar nuestro trabajo a otro servidor o simplemente respaldar pues lo que tenemos que hacer es copiar y/o comprimir (con fecha y hora en curso) dicho fichero. Esto es importante si tenemos la necesidad de apagar o reiniciar nuestro servidor (cosa extraña en ambiente GNU/Linux pero sucede). Ah, por cierto, en el ambiente Etherpad cada documentos es denominado «pad«.
Bitácora de registros de eventos en el lado del servidor Etherpad.
Bitácora de registros de eventos en el lado del servidor Etherpad
Cada usuario conectado o desconectado, nuevo o ya registrado, lo veremos en la ventana terminal de donde lanzamos nuestro servidor Etherpad. Simplemente mirad y admirad la obra de los programadores de software libre.
Configuraciones avanzadas en Etherpad.
Pues algo que nos sorprendió desde un principio fue que no pedía contraseña para abrir o crear un documento o «pad» como se le denomina por acá. Resulta ser que las configuraciones avanzadas las podemos acceder en la carpeta «/admin». Volvemos a nuestro ejemplo en red de área local:
http://192.168.1.47:9001/admin
Una vez allí nos pedirá un usuario y contraseña. Pues simplemente para cambiar las credenciales le damos cancelar y finalizamos el proceso que tenemos corriendo en la ventana terminal donde iniciamos el servidor Etherpad. Luego debemos editar el archivo «settings.json«. Acá os mostramos cómo «tumbamos» el servidor y comenzamos a editar el fichero.
Configurando usuarios en Etherpad
Al abrir veremos cantidad de configuraciones y sis nos ponemos a detallar vemos como importantes las opciones para poder trabajar con protocolo SSL (servidores web seguros https) y para conectar con una base de datos MySql (aunque también pueden ser redis, mongodb, casscandra, MariaDB, etc) pero cambiemos las credenciales del administrador y del usuario que vienen por defecto. Buscamos la siguiente sección y cambiamos los valores.
/* Users for basic authentication. is_admin = true gives access to /admin.
If you do not uncomment this, /admin will not be available! */
/*
"users": {
"admin": {
"password": "changeme1",
"is_admin": true
},
"user": {
"password": "changeme1",
"is_admin": false
}
},
*/
Lo que debemos hacer es descomentar la sección (eliminar «/*» y «*/») y colocamos los valores que queramos, guardamos y «levantamos» de nuevo el servidor con «run.sh«.
Arriba vemos los cambios, si queremos agregar otro usuario que tenga derechos de administrador debemos colocar la variable «is_admin» a «true». Una vez hayamos reiniciado el servicio Etherpad navegamos a la página de configuración, introducimos nuestro usuario y contraseña y veremos la siguiente pantalla:
Accediendo como administrador a Etherpad
Al hacer click en «settings» podremos volver a modificar el archivo «settings.json» vía web e incluso podemos «reiniciar» en caliente el servidor Etherpad.
Modificando settings.json via página web
Cambiando usuarios y «plugins».
Ya en este punto suponemos que estamos lo suficientemente «empapados» de Etherpad por lo tanto ahora os traemos un vídeo donde rápidamente se realiza lo siguiente:
Se descarga e instla Etherpad en un sistema oeprativo privativo.
Se cambian las contraseñas de los usuarios.
Se cambia los «plugins» que queramos (que son bastantes, hay variedad).
En una entrada anterior enseñamos como configurar mod_rewrite en nuestro .htaccess para poner a «punto de caramelo» nuestro Apache2 pero ahora es necesario preveniros de «haber abierto la caja de Pandora»: nuestro servidor web está expuesto a tácticas de intromisión y hasta toma de control por atacantes con malas intenciones. Sirva pues la presente para complementar y ayudaros a proteger vuestros equipos.
Para los que «montamos» páginas web siempre es útil ocultar las extensiones de nuestros archivos a ser servidos y así despistar a posibles atacantes pero es mejor aún para que nuestros usuarios les sea más fácil recordar nuestras secciones e incluso promociones publicitarias, ¿ya tenemos vuestra atención?
Hemos escrito y referenciado -bastante- sobre cómo trabajar bajo la linea de comandos en GNU/Linux y para trabajar con Python de esta manera muchas veces debemos presentar los datos y resultados de una manera agradable a la vista. Para ello echaremos mano de unos comando que vienen por defecto en Python 3.5.2 , la versión que usamos a la fecha. Hagamos pues, ¡manos a la obra!
Introducción.
En el lenguaje de programación Python (y esto es un brevísimo repaso) las cadenas de texto deben ser contenidas o encerradas entre comillas simples o comillas dobles, con apertura y cierre correspondiente y por pares, y dichas comillas no serán mostradas por pantalla al ordenar imprimirlas, veamos el ejemplo:
Hasta aquí no necesita mayor explicación, ustedes decidirán cuáles pares de comillas utilizar, a su gusto y elección pero eso sí, tratemos de ser constantes en su uso, si son simples o dobles. Para este tutorial utilizaremos comillas simples y nuestra balanza se inclina hacia allá porque al uno introducir una cadena de texto entrecomillada doble y presionar intro en el shell de Python por pantalla nos mostrará la misma cadena pero con comilla simples, un detalle a observar. Pero para poder mostrar estas cadenas de caracteres en un guión o script debemos utilizar el comando print() y dentro del par de paréntesis introduciremos dichas cadenas de texto:
Y el resultado por pantalla será la cadena de texto sin comilla alguna.
Concatenado de cadenas de texto.
Si queremos imprimir en una sola línea de texto dos cadenas, debemos usar el símbolo de suma «+» pero en este caso simplemente nos muestra la primera cadena y luego la segunda (podemos incluir en las mismas espacios para separar las palabras):
print('Simón ' + 'José ' + 'Antonio ' + 'de la Santísima Trinidad ' + 'Bolívar y Palacios')
Ahora analizamos que aunque usemos el símbolo de la suma no quiere decir que podemos pasar cualquier número para que sea interpretado como texto, el siguiente comando producirá un error y el segundo es el que debemos utilizar:
print('Simón Bolívar nació en el año ' + 1783)
print('Simón Bolívar nació en el año ' + '1783')
Nótese que hemos encerrado el número entre comillas para que sea considerado una cadena de texto, en el lenguaje Python nos atreveríamos a decir que los datos deber ser explícitos, el lenguaje es estricto en su manipulación (si son caracteres, caracteres serán siempre, si son números igualmente, más adelante profundizaremos en esto).
Repetición de cadenas de texto.
Muchas veces necesitamos repetir uno o más caracteres varias veces, especialmente con propósitos decorativos. Esto es importante en el caso de las licencias de software donde se muchas veces se utiliza el signo de igualdad ‘=’ para encerrar los títulos de la misma. Así como usamos el símbolo de suma ahora utilizaremos el signo de multiplición en programación que es el asteristo ‘*’ (la equis la utilizamos para los humanos, como por ejemplo para representar las tablas de multiplicación). Para ello encerramos entre comillas el texto deseado y seguido de un asterisco y luego un número que representa las veces que repetiremos el caracter o caracteres:
print('='*40)
print('+-'*40)
Almacenando cadenas de texto en variables.
Pues bien, si en nuestro programa vamos a repetir muchas veces una frase pues lo mejor es almacenarla en una variable para mostrarla donde la necesitemos y escribirla una sola vez al principio del software que desarrollemos:
gracias = '¡Muchas gracias por su colaboración!'
Y una vez la tengamos declarada podemos imprimirla con el famoso comando print() -en Python 3 los paréntesis son obligatorios-:
print(gracias)
Reutilizando el último resultado.
Gracias al sr. Juan J. Merelo Guervós quien amablemente publica por Twitter una presentación sobre Python, nos enteramos del uso de el guion bajo «_» para utilizar el último valor que hayamos presentado por la consola interactiva de Python teneindo cuidado de darle el tratamiento adecuado al valor: si es texto, número, etcétera y valga para que aplique a las combinaciones posibles, echad un ojo al siguiente ejemplo:
Uso del guion bajo como almacen del último resultado en consola Python
Comillas y apóstrofos.
Sí, así como lo leen APÓSTROFOS y no , es común caer en este error, pero ese es el nombre correcto: apóstrofos (tal vez influye en errar el como se escribe en inglés: «apostrophes»). Lo cierto del caso es que en castellano no tenemos mayor problema en la escritura, más en el habla coloquial vaya sí que usamos la «contractura» de la palabra (elisión le dice la Real Academia Española), un caso muy común: «para allá» lo pronunciamos «pa’llá». Como véis igual hacemos en Python, solo que esta vez rompemos la regla de utilizar comillas simples porque de lo contrario se produce un error si escribimos ‘pa’llá’ pues la comilla simple en el medio de la frase indica que allí termina la cadena de texto (si queréis ver algo más avanzado, por favor leed nuestro otro tutorial sobre el tema). Algunos nombres que vienen del idioma portugués también utilizan el apóstrofo, como por ejemplo el apellido «D’acosta», y si lidiamos con eso debemos usar distintos tipos de comillas, tengamos precaución con esto al momento de programar.
Si necesitamos mostrar las comillas en sí mismas pues las encerramos debidamente de forma anidada, ejemplo:
print('Sí, eso fue lo que ella dijo:"Hacia el sur"')
Líneas múltiples.
Si pretendemos mostrar varias líneas podemos almacenar dichas líneas encerrandolas entre comillas triples (sencillas o dobles, según necesitemos o queramos):
agua = '''El agua está compuesta de:
-Oxígeno
-Hidrógeno'''
print(agua)
Caracteres de escape.
Con el caracter de escape barra invertida «\» podemos escribir nuestro apóstrofo encerrado entre comillas simples de la siguiente manera:
print('El ciudadano Pedro D\'acosta.')
Como pueden observar le indicamos a Python que tome el caracter que está a la derecha de la barra invertida como caracter en sí mismo y que no lo tome como un caracter especial para el lenguaje (en este caso como delimitador de cadena de texto).
También podemos usar dicho caracter de escape con la comilla doble:
print("Vendedme un tubo de 2\" de diámetro.")
# El siguiente comando también hará el mismo trabajo:
print('Vendedme un tubo de 2" de diámetro.')
En Python si una línea comienza con el símbolo numeral «#», todo lo que esté hacia la derecha hasta el retorno de carro será considerado comentario -sirve para documentar nuestro código para futuras generaciones-.
Además, si el caracter que le agregamos a la derecha NO es un caracter especial (por ejemplo, la letra «n») nos permitirá «quebrar» o insertar un retorno de carro en una sola línea:
print('Línea 1\nLínea 2')
Y con la barra invertida y la letra «t» podremos presentar por pantalla la composición química del agua del ejemplo anterior pero numerada y tabulada:
agua = 'El agua está compuesta de:\n1\tátomo de Oxígeno\n2\tátomos de Hidrógeno'
print(agua)
Y para imprimir la barra invertida…
Y si necesitamos imprimir la barra invertida por pantalla con el comando print() debemos anteceder la letra «r» justo antes de la cadena de texto:
print(r'El agua está compuesta de:\n1\tátomo de Oxígeno\n2\tátomos de Hidrógeno')
Formateando cadenas de texto con marcadores de posición.
Así como utilizamos el comando print() en este caso utilizaremos otro comando intrínseco en Python: format(). En lenguaje Python podemos «heredar» o «unir» a una variable una función -es por ello que dicen y comentan que es un lenguaje de programación avanzado-: por medio del punto «.» unimos primero la variable y luego la función format() auxilíandonos con un par de corchetes «{}» -marcador de posición- que le indicará a dicha función dónde colocar el valor que necesitemos. Vayamos de la teoría a la práctica:
print('Venezuela tiene {} estados.'.format(24))
Y veremos por pantalla lo siguiente:
Venezuela tiene 24 estados.
Tal como lo presentamos es poco útil en verdad, pero vayamos un poco más allá: en Venezuela el ahora estado Amazonas antes era un territorio federal, así que el número de estados a lo largo del tiempo será algo que puede variar. Asimismo muchos países están divididos por estados (otros por comunas como Chile, otros por cantones como Suiza, etcétera). Veamos el siguiente código:
pais = "Venezuela"
estados = 24
print(pais + ' tiene {} estados.'.format(estados))
Ahora tiene más sentido ya que dichas variables ‘pais’ -sin acento, así se acostumbra escribir los nombres de variables- y ‘estados’ podrían ser llenados, por ejemplo, desde los valores almacenados en una base de datos con varios países y sus respectivas cantidades de estados y podremos hacer un ciclo para mostrarlos a todos. Lo que incluiremos entre corchetes no necesariamente debe ser un número, también puede ser otra cadena de texto como veremos a continuación.
Formateando cadenas de texto con varios marcadores de posición.
Continuando con el ejemplo anterior, es posible reescribirlo de la siguiente manera:
pais = "Venezuela"
estados = 24
print('{} tiene {} estados.'.format(pais, estados))
Siempre hay que tener en cuenta el colocar las variables a insertar en el mismo orden que van de izquierda a derecha según los marcadores de posición, esto es así porque dentro de los corchetes no hemos insertado aún órdenes especiales; por lo tanto lo siguiente NO mostrará correctamente la información:
pais = ''Venezuela'
estados = 24
print('{} tiene {} estados.'.format(estados, pais))
Para corregir lo anterior debemos colocar el número correspondiente (comenzando desde cero) a la enumeración de elementos -valga la redundancia-. Es decir que lo que insertemos dentro de los paréntesis del comando format -separados por comas- automáticamente tomará el primer elemento el índice cero, el segundo, uno y así sucesivamente:
pais = "Venezuela"
estados = 24
print('{1} tiene {0} estados.'.format(estados, pais))
Podremos escribir muchos más elementos que la cantidad de marcadores de posición -corchetes {}- pero no menos, porque produce un error; veamos el próximo modelo:
pais = "Venezuela"
print('{} tiene {} estados.'.format(pais))
Allí tenemos dos marcadores de posición pero una sola variable (podemos complicar el ejercicio con muchos más elementos, pero trabajar con 2 ó 3 elementos ilustra bien lo que queremos enseñar). Igualmente, si a los marcadores de posición le insertamos índices que no existen, pues también nos arroja error:
pais = "Venezuela"
estados = 24
print('{1} tiene {2} estados.'.format(estados, pais))
Tal como les indicamos, se debe enumerar desde cero, es así que el elemento 2 no existe ni está declarada dicha variable (está fuera de rango, produce error).
Si se nos hace difícil el trabajar con base cero, también Python nos permite colocarles nombres clave a las variables abramos nuestra mente a los llamados alias:
pais = "Venezuela"
estados = 24
print('{uno} tiene {dos} estados.'.format(uno = estados, dos = pais))
Si analizamos bien, en realidad estamos llamando a la variable «pais» con el alias «uno» y «estados» con el alias «dos», lo cual nos muestra lo poderoso que es el lenguaje Python.
Pasando los formatos específicos.
Hasta ahora hemos utilizado los marcadores de posición vacios o con un índice o alias pero podemos ir más allá. Por medio de los dos puntos «:» nos permite separar el índice o alias del formato con que queramos presentar las variables. Para ilustrar imaginemos que tenemos una lista de países pero sus nombres, desde luego, no tienen el mismo número de caracteres. Si queremos mostrarlos de una manera muy ordenada nos fijaremos cual tiene la cifra más larga y asi dejaremos sufiente espacio para los otros más cortos. Hagamos un sencillo listado de 3 países hartos conocidos por nosotros los venezolanos:
pais = ("Colombia", "Chile", "Venezuela")
division = ("departamentos", "comunas", "estados")
Con estas declaraciones le estamos indicando a Python que nos haga una matriz (ellos lo llaman «tupla», una palabra no reconocida por la RAE) de 1 fila y 3 columnas para la variable «pais» e igualmente para la variable «division». Fijaos que el más largo es Venezuela con 9 letras y le sumaremos uno más para separar del resto de la oración con un espacio; haremos uso de un ciclo for:
for k in range(0,3):
print("{0:10} está dividido políticamente en {1}.".format(pais[k], division[k]))
Ya os dijimos que las numeraciones arrancan desde cero, pero es conveniente aclarar que la variable «k» es evaluada por Python al inicio del ciclo, por eso debemos agregarle uno más a nuestros índices de matriz. Veremos la siguiente salida por pantalla:
Colombia está dividido políticamente en departamentos.
Chile está dividido políticamente en comunas.
Venezuela está dividido políticamente en estados.
¡Qué práctico! Lo más interesante es que podemos alinear las variables al centro o a la derecha (por defecto alinea a la izquierda) si le colocamos el signo «^» o «<» para la alineación a la izquierda o «>» para alinear a la derecha:
for k in range(0,3):
print("{0:10} está dividido políticamente en {1}.".format(pais[k], division[k]))
Colombia está dividido políticamente en departamentos.
Chile está dividido políticamente en comunas.
Venezuela está dividido políticamente en estados.
Incluso podemos presentar los nombres de los países de la misma manera que uno hace al escribir los nombres de los beneficiarios en un cheque bancario: agregando asteriscos (o cualquier otro caracter) a la izquierda y derecha y centrando el nombre y agregaremos 12 «espacios» en vez de 10, probad en vuestra terminal lo siguiente (no publicamos imágenes del resultado para «forzaros» a practicar en vuestro ordenador):
for k in range(0,3):
print("{0:*^12} está dividido políticamente en {1}.".format(pais[k], division[k]))
Tal vez con los números hallaremos la máxima utilidad al formateo de cadenas con marcadores de posición, veamos algunos de ellos.
Pasando los formatos numéricos específicos.
Muchas veces necesitamos en nuestros programas las tasas de los impuestos que recauda el Estado para el buen funcionamiento de la República. En Venezuela el ente encargado es el Servicio Nacional Integrado de Administración Tributaria y Aduanera (SENIAT por sus siglas) quien legalmente está facultado a publicar y hacer cumplir las variaciones en los porcentajes. El Impuesto al Valor Agregado (IVA por sus siglas) recordamos que comenzó con un 10% en 1994 (año en que fue creado el SENIAT de la mano de la Guardia Nacional quienes nos visitaron para cumplir y hacer cumplir el impuesto) y ha sufrido subidas y bajadas estando hoy en 12% -en España tenemos entendido que está en 25% al momento de escribir estas líneas-. Imaginemos que queremos informar esto a nuestros usuarios:
iva1994 = 10
iva2016 = 12
print('La tasa de impuesto del IVA en 1994 era de {:f}%'.format(iva1994))
print('Hoy en día, año 2016:{:f}%'.format(iva2016))
Como recordaremos, en el marcador de posición debemos colocar los dos puntos para separar el índice (por defecto cero porque es un solo elemento y se puede omitir) del formato numérico que deseamos presentar. En nuestra vida común los números del 0 al 100 toman especial importancia y tanto nuestra moneda y tasas se representa hasta con dos decimales a la derecha de la coma -mejor dicho, empleamos hasta la centésima parte de la unidad-. Es por esto que, si habéis practicado los comandos de arriba, notaréis que se «imprime» con seis decimales porque utilizamos el parámetro «f» -número con separador decimal flotante- y para limitar la salida a dos decimales debemos agregar «.2» al formato:
print('La tasa de impuesto del IVA en 1994 era de {:.2f}%'.format(iva1994))
print('Hoy en día, año 2016:{:.2f}%'.format(iva2016))
Ahora se ve un poco mejor, pero con el inconveniente de que nuestro separador decimal es la coma y para el mundo anglosajón es el punto (los sistemas operativos modernos lidian con esto muy bien todo el tiempo simplemente asignandole un identificador especial que tal vez sea el Unicode 2396 «⎖» y mostrando al usuario de cada región su símbolo particular en cada salida por pantalla y/o impresora). Nota: los parámetros en la función format() deben ser en estricto orden: «{0:.2f}» es lo correcto -índice, separador, número de decimales y tipo de número, «f» para flotante-; mientras que «{0:f.2}» no funcionará.
Usando formatos de texto para presentar datos.
Una aplicación práctica de la vida real es mostrar a nuestros párvulos las tablas de multiplicar, escojamos -¡ejem!- el número 7, nuestro número de la suerte, y hagamos un programita:
for k in range(1,10):
print('{} x {} = {}'.format(7, k, 7*k))
Como véis no representa mayor problema la salida pues el único elemento díscolo es el resultado de «7 x 1», vamos a ampliar la tabla hasta 30 (recordad sumar uno más) y mostramos su respuesta a continuación:
for k in range(1,31):
print('{} x {} = {}'.format(7, k, 7*k))
7 x 1 = 7
7 x 2 = 14
7 x 3 = 21
7 x 4 = 28
7 x 5 = 35
7 x 6 = 42
7 x 7 = 49
7 x 8 = 56
7 x 9 = 63
7 x 10 = 70
7 x 11 = 77
7 x 12 = 84
7 x 13 = 91
7 x 14 = 98
7 x 15 = 105
7 x 16 = 112
7 x 17 = 119
7 x 18 = 126
7 x 19 = 133
7 x 20 = 140
7 x 21 = 147
7 x 22 = 154
7 x 23 = 161
7 x 24 = 168
7 x 25 = 175
7 x 26 = 182
7 x 27 = 189
7 x 28 = 196
7 x 29 = 203
Aquí está el meollo del asunto, a medida que los números «engordan» nuestras columnas no se presentan de manera tabulada. Para observarlo más a nuestro gusto, el de los seres humanos (imagino que las computadoras cuando gobiernen al mundo obviarán el arte por completo, privando la funcionalidad por encima de todo, pero hasta que esos días lleguen no nos preocuparemos por #Skynet) necesitamos que cada número, sin importar su valor, ocupe máximo 3 espacios o casillas, si lo queremos ver de esa manera. Para ello agregamos «{:3d]» en cada marcador de posición y listamos el resultado (en este ejemplo colocamos lo que se ve en la línea de comandos de Phyton):
Python ciclo for con marcadores de posicion para una tabla de multiplicar.
¡Mucho mejor para nosotros los obsesivos por el orden!
Formatos soportados por Python.
Como era de esperarse, hay una gran cantidad de parámetros en el comando format() y acá explicamos los hasta ahora conocidos teniendo en cuenta que hay que respetar el orden en que pasamos los parámetros, a saber son los siguientes:
El índice o alias de la variable que queremos formatear, generalmente se omite ya que acostumbramos a colocarlas en el orden correspondiente, cosas de nosotros los seres humanos.
Caracter separador, los dos puntos «:».
Caracter de relleno, cualquiera que se necesite (hicimos un ejemplo con un asterisco, el ejemplo de los cheques bancarios ¿lo recordáis?).
Alineación (recordad el ejemplo de los países), abajo especificamos más.
Signo positivo, negativo o cero (abajo aclaramos).
En el caso de números en otros sistemas de numeración podemos usar «#» o «0», de nuevo abajo explicamos mejor el asunto.
Opción de agrupamiento: aquí si que hay mucha tela que cortar, mirad el punto 8 que lo hacemos muy detalladamente.
Precisión: en el caso de las tasas de impuesto usamos 2 decimales, pero podemos usar los que necesitemos, lo único que no explicamos es que esta función redondea hacia arriba (si es mayor o igual a 5) o hacia abajo según el primer decimal fuera del alcance de precisión.
Tipo de dato: lo más importante queda de último, ya que podemos indicarle explícitamente qué tipo de datos pasamos e incluso que conversión hacemos, leed el punto 10 en detalle.
Los puntos 1 y 2 ya los hemos explicado muy bien, veamos los siguientes.
3.-Caracter de relleno.
Cualquier caracter INCLUSO los caracteres especiales no presentan ningún problema, recordad que lo TODO lo que está en el marcador de posición «{}» Python los considera parámetros, no comandos especiales; haced la prueba colocando comillas simples, dobles, barra y barra invertida y os lo mostrará sin problema alguno.
4.-Alineación de caracteres.
«<«: alinea a la izquierda, generalmente se omite ya que es la opción por defecto en la mayoría de los objetos.
«^»: alinea al centro del espacio disponible.
«>»: alinea a la derecha.
«=»: solamente para formatos numéricos que presentan signo, por ejemplo «+000000120», rara vez se utiliza.
5.-Signo numérico.
«+», «-» y » «: si necesitamos colocarle signo a nuestro resultado usamos el signo de suma o de resta, o simplemente dejamos un solo espacio en blanco reservado para el signo, si es negativo se muestra sino coloca un espacio en blanco.
6.-Forma alterna para la conversión.
El utilizar «#» o «0» nos hes tremendamente útil si hacemos conversiones a otros sistemas de numeración (binarios, por ejemplo), mirad el punto 10.
7.-Anchura del campo.
Ya dijimos que está limitado por nuestra noció de estética pero en realidad si le pasamos un valor muy alto pues simplemente Python lo mostrará en tantas líneas como necesite, una abajo de la otra, dando al traste cualquier tipo de formato que deseemos, cuidadito con esta cifra.
8.-Opción de agrupamiento.
¿Recuerdan que hablamos del separador decimal y que en nuestro país es la coma? De hecho nosotros usamos el punto como separador de miles y esto trae muchas veces un dolor de cabeza para el mundo de la banca y negocios. Acá os mostramos cómo lo maneja Ubuntu 16.04:
Formato regional en Ubuntu 16.04
Pues acá va que Python se empeña en utilizar el punto como separador decimal y la coma como separador de miles a pesar de nosotros tener nuestra configuración regional como corresponde. Es por ello que en la PEP378 le buscan una solución al asunto haciendo uso de un comando y un artilugio: cambiamos las comas «,» que separan los miles en formato anglosajón por guiones bajos «_», luego cambiamos el punto «.» que es separador decimal en formato anglosajón por una coma «,» y finalmente cambiamos los guiones bajos «_» por puntos «.» ¿complicado en idioma castellano? Veamos como se escribe en lenguaje Python:
monto = 1234567.89
print('Su saldo bancario es Bs.' + '{:,}'.format(monto).replace(",","_").replace(".", ",").replace("_","."))
El comando clave es replace(,) y nótese que hemos dejado el símbolo de «Bs.» fuera del valor numérico para que ese punto no sea sustituido por una coma y nuestro trabajo se ve plasmado así:
Representación correcta de Python de los formatos numéricos para Venezuela.
¿Complicado? Pues aún no hemos comenzado a programar de verdad, ja ja ja 😉
En realidad nuestros amigos que desarrollan Python de manera desinteresada (monetariamente hablando) se han compadecido de nuestro predicamento porque, al menos nos han permitido el parámetro de la coma «,» como separador de miles, de parte de nosotros está comenzar a programar nuestras propias utilerías y «subirlas» a GitHub para que estén disponibles de manera pública por medio del Software Libre. Como ustedes ya imaginarán ya otros colegas habrán pasado por esto y en la internet habrá una solución ya realizada, lo que hay es que buscarla o sino hacerla nosotros mismos.
10.-Tipos de datos (y conversiones).
Pues ya va finalizando nuestra clase del día, lo último y tal vez más importante es que le podemos decir a Python el tipo de dato que le vamos a pasar para que nos lo represente adecuadamente e incluso podemos hacer conversiones a otros sistemas de numeración, miremos:
«s»: formato de cadena de texto, es el valor por omisión, eso quiere decir que es tácito y sobre entendido que lo que le pasamos al comando format() es una cadena de texto sin más; se puede omitir.
«b»: nos representará un número en de numeración formato binario, si le pasamos la coma como separador de agrupamiento lo hará de 4 en 4 caracteres.
«c»: un número entero que corresponde a un caracter ASCII, así la letra «A» es el número 34, le pasamos ese número y nos dibujará una letra «A».
«d»: sistema de base decimal, si se lo pasamos en binario hace la conversión correspondiente.
«o»: sistema de numeración de base 8.
«x» y «X»: sistema de numeración de base 16 con los caracteres en mayúsculas o minúsculas para los valores por encima de nueve.
«n»: supuestamente para indicar que se utilize los separadores decimales y sepradores de miles segúna la configuración regional del sistema operativo del usuario (lo probamos y no funciona para nada bajo Python 3.5.2, no señor, la teoría es hermosa pero la práctica que hace nuestra experiencia ES MARAVILLOSA).