Python logo sin texto

Python 3: tutorial argparse

En el tutorial anterior sobre registro de eventos con “loggingutilizamos 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:

sudo apt-get install python3-setuptools
sudo easy_install3 pip

Por medio de su código fuente.

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:

usage: tutorial_argparser.py [-h] [-a A]
tutorial_argparser.py: error: argument -a: expected one argument

De nuevo metemos

python3 tutorial_argparser.py -a ¡Hola!

y veremos justo lo que le ordenamos hacer:

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:

$ python3 tutorial_argparser.py -a "¡Hola mundo!"

Argumento opcional solicitado: --aviso
Argumento acompañado de:¡Hola mundo!

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…

Argumentos opcionales mutuamente excluyentes.

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?

Fuentes consultadas:

En idioma inglés:

YouTube:

 

Python logo sin texto

Python 3: llevando una bitácora de programa

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

class calculadora():

  def __init__():
    print("\nCalculadora encendida.")

  def sumar( a=0, b=0):
    print("Suma a={} b={} a+b={}".format(a,b,a+b))

  def restar( a=0, b=0):
    print("Resta a={} b={} a-b={}".format(a,b,a-b))

  def multiplicar( a=0, b=0):
    print("Multiplicación a={} b={} a*b={}".format(a,b,a*b))

  def dividir( a=0, b=1):
    print("División a={} b={} a/b={}".format(a,b,a/b))

calc = calculadora
calc.sumar( 12,4)
calc.restar(12,4)
calc.multiplicar(12,4)
calc.dividir(12,4)

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:

import logging
logging.basicConfig(level=logging.DEBUG)

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.

import logging
logging.basicConfig(level=logging.DEBUG)

class calculadora():

  def __init__(self, nombre):
    self.nombre = nombre
    logging.debug("\nCalculadora modelo {} (encendida).".format(self.nombre))

  def sumar(self, a=0, b=0):
    logging.debug("Suma           a={} b={} a+b={}".format(a,b,a+b))

  def restar(self, a=0, b=0):
    logging.debug("Resta          a={} b={} a-b={}".format(a,b,a-b))

  def multiplicar(self, a=0, b=0):
    logging.debug("Multiplicación a={} b={} a*b={}".format(a,b,a*b))

  def dividir(self, a=0, b=1):
    logging.debug("División       a={} b={} a/b={}".format(a,b,a/b))

calc = calculadora("A1")
calc.sumar( 12,4)
calc.restar(12,4)
calc.multiplicar(12,4)
calc.dividir(12,4)

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

import logging
logging.basicConfig(level=logging.DEBUG)

bita_sum = logging.getLogger("Sum")
bita_res = logging.getLogger("Res")
bita_mul = logging.getLogger("Mul")
bita_div = logging.getLogger("Div")

class calculadora():

  def __init__(self, nombre):
    self.nombre = nombre
    logging.debug("\nCalculadora modelo {} (encendida).".format(self.nombre))

  def sumar(self, a=0, b=0):
    bita_sum.debug("Suma           a={} b={} a+b={}".format(a,b,a+b))

  def restar(self, a=0, b=0):
    bita_res.debug("Resta          a={} b={} a-b={}".format(a,b,a-b))

  def multiplicar(self, a=0, b=0):
    bita_mul.debug("Multiplicación a={} b={} a*b={}".format(a,b,a*b))

  def dividir(self, a=0, b=1):
    bita_div.debug("División       a={} b={} a/b={}".format(a,b,a/b))

calc = calculadora("A1")
calc.sumar( 12,4)
calc.restar(12,4)
calc.multiplicar(12,4)
calc.dividir(12,4)

Acá vemos la salida correspondiente:

salida python3 calculadora2.py
salida python3 calculadora2.pyz

Grabando mensajes en un archivo: bitácora.log

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:

logging.basicConfig(
  filename="bitacora.log",
  level=logging.DEBUG,
)

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

logging.basicConfig(
  filename="bitacora.log",
  level=logging.DEBUG,
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

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

logging.basicConfig(
  filename="bitacora.log",
  level=logging.DEBUG,
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
  datefmt='%d/%m/%Y %I:%M:%S %p'
)

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:

NivelValor
numérico
FunciónUso
NOSET0no aplicano aplica
DEBUG10logging.debug()Diganóstico de problemas, muestra información bien detallada.
INFO20logging.info()Confirma que todo está funcionando correctamente.
WARNING30logging.warning()Indica que algo inesperado ha sucedido, o pudiera suceder.
ERROR40logging.error()Indica un problema más serio.
CRITICAL50logging.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 CRITICAL ya 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
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
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-:

  • getopt es una librería la cual se deriva de una del lenguaje C llamada, claro, getopt().
  • optparse escrita 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:

  1. 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).
  2. 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.
  3. 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).
  4. 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-buscarcomo 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.

import logging
import argparse

analizador = argparse.ArgumentParser("Programa demostrativo de argparse")
analizador.add_argument("-log_lev", help="Utilice DEBUG, INFO, WARNING, ERROR o CRITICAL")
analizador.parse_args()
argumentos = analizador.parse_args()
if argumentos.log_lev == 'DEBUG':
 logging.basicConfig(level=logging.DEBUG)
elif argumentos.log_lev == 'INFO':
 logging.basicConfig(level=logging.INFO)
elif argumentos.log_lev == 'WARNING':
 logging.basicConfig(level=logging.WARNING)
elif argumentos.log_lev == 'ERROR':
 logging.basicConfig(level=logging.ERROR)
elif argumentos.log_lev == 'CRITICAL':
 logging.basicConfig(level=logging.CRITICAL)
else:
 logging.basicConfig(level=logging.WARNING)
# Opción no válida, se establece nivel WARNING por defecto.


logging.basicConfig(
 filename="bitacora.log",
 format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
 datefmt='%d/%m/%Y %I:%M:%S %p'
)

bita_sum = logging.getLogger("Sum")
bita_res = logging.getLogger("Res")
bita_mul = logging.getLogger("Mul")
bita_div = logging.getLogger("Div")

class calculadora():

def __init__(self, nombre):
 self.nombre = nombre
 logging.debug("\nCalculadora modelo {} (encendida).".format(self.nombre))

def sumar(self, a=0, b=0):
 bita_sum.info("Suma a={} b={} a+b={}".format(a,b,a+b))

def restar(self, a=0, b=0):
 bita_res.info("Resta a={} b={} a-b={}".format(a,b,a-b))

def multiplicar(self, a=0, b=0):
 bita_mul.info("Multiplicación a={} b={} a*b={}".format(a,b,a*b))

def dividir(self, a=0, b=1):
 if (b==0):
 bita_div.error("Alerta: el divisor debe ser distinto a cero.")
 else:
 bita_div.info("División a={} b={} a/b={}".format(a,b,a/b))

calc = calculadora("A1")
calc.sumar( 45,5)
calc.restar(45,5)
calc.multiplicar(45,5)
calc.dividir(45,0)

Fuentes consultadas:

En idioma inglés:

Python logo sin texto

Python http.server

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.

Python logo original
Python logo original

Introducción.

Pasar ficheros de una máquina a otra en algún momento nos tocó o tocará hacerlo y en nuestro caso fue de una máquina con Ubuntu hacia otra con software privativo ubicada fuera de nuestra red de área local. Para ello habilitamos un servidor FTP, en este caso ProFTPd, que conecta con las credenciales de otro usuario creado de manera exprofeso y con derechos de lectura sobre los archivos en cuestión. Eso a nosotros nos pareció fácil, pero ¿qué tal hacer algo que se pueda ejecutar en varios sistemas operativos?

Para ello nada mejor que Python: es legendario y ya en 1996 corría en sistemas tan diverso como Linux, IRIX, Compaq Tru64, OS X, Solaris, and Windows (ver caso de éxito “Industrial Light & Magic”, “Lucas Films LTD”). Al momento de escribir estas líneas Python (que ya va por la versión 3.6.1) está disponible para ser descargado y usado en Linux/Unix, Windows, AS/400 (OS/400), Beos, MorphOS, MS-DOS (sí, así como lo leen, no ha muerto, aún), OS/2, OS/390 and z/OS, RIC-OS, Symbian OS series 60, VMS, ¡¡¡ Windows CE or Pocket PC !!!, HP-UX y hasta podemos comprar una versión privativa a la cual no podremos acceder a su código fuente, y cuya distribución es llamada ActivePython (sabrá Dios en cuales plataformas de hardware ejecutará este último “sabor”) pero como véis, de que tenemos opciones y libertad de escoger es indudable, y si nos proponemos hasta podemos nosotros mismos hacer nuestra propia distribución de Python por nosotros elaborada (palabras mayores). Esto último es conocido como “implementaciones” y las podéis conocer más en este enlace -en idioma inglés-; ya por último debemos recalcar que Python es aceptado como lenguaje dentro del manejador de base de datos PostgreSQL ¡¿maravilloso, cierto?! 😎 .

Versión Python 2.x

En dicha versión 2.x se utiliza “SimpleHTTPServer” y lo probamos y comprobamos desde nuestra red de área local y desde una máquina externa, ya en la entrada que nos inspiró bien lo explican: solo debemos lanzar el siguiente comando (las librerías vienen integradas en Python):

python -m SimpleHTTPServer 8000

¿Fácil cierto? Nos vamos en una ventana terminal hasta la carpeta que contiene los archivos “a compartir”, lanzamos el guión en Python indicandole que abrimos el puerto 8000 y en el otro ordenador lanzamos nuestro navegador web con la dirección IP destino (si tenemos un enrutador pues configurad ALGO parecido a lo nuestro):

TP-LINK Forwarding Virtual Servers
TP-LINK Forwarding Virtual Servers

Reconocimiento a la labor del sr. Elías.

En su repositorio de aplicaciones -gentilmente alojadas por GitHub.com- el sr. Elías nos enseña como agregar valor al código existente por medio de una bonita interfaz gráfica para escoger la carpeta a compartir y cambiar el puerto de escucha -vamos que antes igual teníamos esa opción por la línea de comandos- pero con otro botón adicional: el botón de detener el servicio ya que por línea de comandos solo con CTRL+C se detenía el proceso. En la siguiente imagen comparto una carpeta de respaldo de un antiguo aparatito reproductos de MP3 y disfruto de música que tenía tiempo que no escuchaba:

PyShare (guion escrito por Elías Rodríguez Martín)
PyShare (guion escrito por Elías Rodríguez Martín)

Lo bonito del asunto del asunto (el guion, no la utilería gráfica PyShare) es que podemos navegar en nuestras carpetas, iniciar el servicio, detenerlo, ir a otra carpeta y repetir el proceso; y por si queda alguna duda sobre su funcionamiento se abre de una vez una pestaña en nuestro navegador web predeterminado para visualizar la carpeta compartida, todo muy práctico:

python_easy_server.py
python_easy_server.py

Lo que véis en la última imagen es el guión desarrollado por el Sr. Elías y el último error es simplemente un mal manejo de los caracteres de nuestro bello idioma castellano (compartí la carpeta llamada “Vídeos” que tiene un acento). Alegremente lo primero que hisimo fue lo que hacen los niñatos y niñatas de los parvularios, COPIAR Y PEGAR en un archivo llamado “python_easy_server.py” pero obviando la regla de oro: UTILIZAR GIT PARA CONTROLAR (y colaborar) EN EL CONTROL DE VERSIONES (mirad nuestro humilde tutorial al respecto).

No os vamos a “aburrir” con el tema de GitHub pero si os indicamos que propusimos unos cambios (“fork” y “pull request”) que podréis analizar con detalle en nuestros repositorios alojados -de nuevo, la publicidad- en GitHub.

Versión Python 3.X

Como bien especifica el sr. Elías en Python versión 3.x el SimpleHTTPServer ha sido sustituido por http.server y en esencia tiene la misma línea de comandos pero si analizamos el código pues bueno, todo de acuerdo a la nueva versión (3.6 en este caso). En el repositorio correspondiente en el módulo server.py leemos 1211 líneas y está muy bien explicado, un trabajo muy compacto con las deficiones básicas del protocolo HTTP y sobre cómo funciona por defecto esta librería escrita en Python.

Nos agrada mucho que vayan directo y al grano explicando cómo funciona un servidor web, eso es poesía informática, para muestra un botón: el inicio de la clase BaseHTTPRequestHandler:

The following explanation of HTTP serves to guide you through the
code as well as to expose any misunderstandings I may have about
HTTP (so you don’t need to read the code to figure out I’m wrong
:-).

Que traducido lo exponemos de la siguiente manera:

La siguiente explicación de HTTP sirve para guiarlo a través del código, así como para exponer cualquier malentendido que yo pueda tener sobre HTTP (por lo que no necesita leer el código para averiguar que estoy equivocado).

Allí se fajan a explicar cómo un servidor web recibe una petición y como se contesta, ya sea con el protocolo “HTTP/0.9” (por defecto en este módulo, línea N° 261), “HTTP/1.0” o “HTTP/1.1”. Esencialmente funciona de la siguiente manera (vamos a ser redundantes con propósitos didácticos):

  1. Un servidor web está funcionando con un “socket” abierto que se interpreta (y nos abstraemos) como un puerto abierto en un número conocido (por defecto es el puerto 80 pero como estamos usando algo “privado” usamos el puerto 8000).
  2. Un programa cliente, ya sea en nuestra propia red de área local o desde el internet, envía una petición a ese puerto conociendo de antemano la dirección IP del servidor web (no ahondaremos en el tema de los DNS, para los lectores más avezados).
  3. Esa petición tiene un protocolo específico: ejemplo “GET / HTTP/1.1″ ¿Pero que significa esto? Veamos el siguiente punto no sin antes mencionar que una petición puede contener dos líneas adicionales que son opcionales (por ahora no complicaremos la labor).
  4. Ante una petición tipo GET (en este caso nos exigen usar un protocolo específico HTTP/1.1) el servidor web procede a revisar el directorio por defecto (estamos hablando del http.server de Python, un servidor Apache es mucho más avanzado) y si encuentra un archivo index.html lo enviará, de lo contrario, si no lo halla entonces procederá a mostrar un listado de los archivos presentes en la carpeta desde donde ejecutamos el comando “python -m http.server 8000 (de hecho así también se comporta un servidor web Apache). Este último comportamiento lo podemos detallar en la función send_head() de la clase SimpleHTTPRequestHandler. Incluso podríamos aquí empoderarnos de nuestro idioma e incluir comandos para buscar también un archivo llamado “índice.html“, es decir, que ‘busque’ en inglés y castellano ¿Por qué no? (amén de traducir al castellano todos los demás mensajes). Así mismo, con acentos y todo el hecho de nosotros hablar dos o más idiomas ejercita nuestro cerebro, ampliamos nuestra memoria y nos ayuda a crear sinapsis nuevas en nuestras neuronas.
  5. La manera como envía esta respuesta debe estar ajustada al protocolo en el cual nos lo solicitaron. Para este ejemplo debemos contestar según el protocolo que nos pidieron [más abajo veremos que esto no es necesariamente cierto;-) ] y el cual consta de tres partes.
  6. La primera parte es una línea que está compuesta a su vez en tres segmentos:
    • Versión en la que estamos respondiendo (este ejemplo “HTTP/1.0“) más un espacio como separador al final.
    • Un código numérico de tres dígitos que está bien normalizado ( ¡A que alguna vez habrán visto una respuesta “404“! ). Para este ejercicio contestaremos con un “200” (más el consabido espacio en blanco como separador).
    • Un tercer segmento que explica ¡cómo no, en inglés! un mensaje descriptivo para nosotros los seres humanos (el código anterior, numérico, es muy agradable a nuestros amigos los ordenadores). El código 200 tiene la descripción simple y llana: OK.
  7. Para separar la primera parte de la segunda se recomienda utilizar la combinación de caracteres especiales de retorno de carro y avance de línea, pero debemos estar prestos a utilizar solo el avance de línea para una compatibilidad más amplia (de nuevo, ver BaseHTTPRequestHandler).
  8. La segunda parte son varias líneas llamadas encabezados y ajustados a la norma RFC-822 (la norma data del año 1982 pero pensamos está plenamente vigente).
  9. Un punto muy especial es que para separar la segunda parte de la tercera, además de utilizar el CRLF -ver punto 7- debemos insertar una línea en blanco, ¿cómo se hace esto? Pro favor ver la función end_headers() {línea 516} y os sorprenderéis.
  10. La tercera parte son los preciados datos en sí mismos cuyas características están especificadas de antemano en la segunda parte (recordad RFC-822).

Todo esto QUE PARECE COMPLICADO EN TEORÍA os lo podemos demostrar en la práctica: abrimos una ventana terminal y ejecutamos el servidor web Python, abrimos una segunda ventana terminal y ejecutamos la siguiente orden con el parámetro -i (o la manera larga –include) el cual muestra los encabezados HTTP:

curl -i http://192.168.1.47:8000/

Tras lo cual recibiremos la siguiente respuesta:

HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.5.2
Date: Sun, 16 Apr 2017 01:24:24 GMT
Content-type: text/html; charset=utf-8
Content-Length: 1370

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
(...

Acá la “magia” la realiza nuestro navegador web: según el Content-type se prestará a interpretar y mostrar de manera correcta los datos recibidos. Si es “text/html” pues vemos que es el lenguaje de marcado muy famoso y que además debemos mostrarlo con codificación utf-8. ¿Estáis confundido o perdido? Consultad nuestro tutorial sobre HTML.

Notad la respuesta del servidor identificandose a sí mismo “SimpleHTTP/0.6 Python/3.5.2“, maravilloso para nosotros. Mirad en la siguiente figura a lo que nos referimos:

Orden «GET HTTP 1.1»
Orden «GET HTTP 1.1»

Solicitando descargar un archivo.

Sigamos entonces con nuestra práctica: ya sabemos que no vamos a mostrar un archivo index.html -ni índice.html– sino que mostramos un listado de archivos, ahora nos iremos a la carpeta donde clonamos el “fork” del proyecto py-qt-share y lanzamos allí nuestro servidor web Python.

«python3 -m http.server 8000» carpeta proyecto py-qt-share
«python3 -m http.server 8000» carpeta proyecto py-qt-share

Allí tenéis de una vez la tres líneas completas, un abreboca, pero veamos la primera y segunda líneas -olvidemos la tercera, por el momento- y abramos de nuevo otra ventana terminal donde lanzamos el comando curl para retribuir el listado de archivos disponibles:

orden GET HTTP 1 sobre carpeta py-qt-share
orden GET HTTP 1 sobre carpeta py-qt-share

Una vez recibimos e interpretamos el HTML solicitamos de nuevo con otro comando GET el fichero README.md, observemos lo que responde:

curl -i README.md
curl -i README.md

Diseccionemos la respuesta:

Server: SimpleHTTP/0.6 Python/3.5.2
Date: Sun, 16 Apr 2017 02:55:23 GMT
Content-type: application/octet-stream
Content-Length: 575
Last-Modified: Sat, 15 Apr 2017 23:33:16 GMT

¿Cómo es posible que en el Content-type nos indique que es una aplicación? 😐😐😐

Sabemos plenamente que un fichero con extensión .md generalmente contiene un lenguaje de marcado tipo MarkDown (de nuevo, mirad nuestro otro tutorial relacionado), si acaso se asemeja más a un text/html que a una aplicación ¿Qué piensan ustedes?

Los lectores más experimentados nos responderán “¿de dónde saca que .md es un MIME type?” Pues veamos los tipos principales definidos en RFC-2046:

The “text” media type is intended for sending material which is
principally textual in form. A “charset” parameter may be used to
indicate the character set of the body text for “text” subtypes,
notably including the subtype “text/plain”, which is a generic
subtype for plain text.

De manera independiente a la opinión que tengamos si MarkDown es un fichero de texto o una aplicación debemos primero analizar en qué se basan para programar nuestro servidor web en Python para clasificar los archivos que listamos para compartir con otras computadoras. Analizemos la función dedicada a esto:

  def guess_type(self, path):
     """Guess the type of a file.
     Argument is a PATH (a filename).
     Return value is a string of the form type/subtype,
     usable for a MIME Content-type header.
     The default implementation looks the file's extension
     up in the table self.extensions_map, using application/octet-stream
     as a default; however it would be permissible (if
     slow) to look inside the data to make a better guess.
     """

Como bien podemos leer PRIMERO se trata de identificar el archivo por la extensión del archivo, lo cual es una solución rápida -y sucia- de identificar con qué tipo de datos estamos lidiando, y por defecto asumimos que es una aplicación “application/octet-stream” ya que ponerse a la tarea de abrir cada archivo y ver su contenido es una tarea muy tediosa (y que puede bloquear nuestro ordenador si tenemos demasiados ficheros almacenados).

“import mimetypes”

Es por ello necesario que pasemos a revisar la librería que importamos llamada mimetypes. Si revisamos la documentación de Python al respecto pues… la metodología es la misma, ¡”adivinar” por la extensión del fichero!

No obstante se apoyan en un método más “científico”: revisar las aplicaciones que tenemos en nuestro sistema operativo para poder ofrecer así una mayor variedad de opciones:

knownfiles = [
 "/etc/mime.types",
 "/etc/httpd/mime.types", # Mac OS X
 "/etc/httpd/conf/mime.types", # Apache
 "/etc/apache/mime.types", # Apache 1
 "/etc/apache2/mime.types", # Apache 2
 "/usr/local/etc/httpd/conf/mime.types",
 "/usr/local/lib/netscape/mime.types",
 "/usr/local/etc/httpd/conf/mime.types", # Apache 1.2
 "/usr/local/etc/mime.types", # Apache 1.3
]

Actualizado el jueves 20 de julio de 2017.

En este enlace de la “Web del Programador” podreis descargar un fichero en formato PDF con la mayoría de las extensiones de fichero conocidas, muy completa lista, os lo recomendamos.


De acá notamos que rapidamente podemos agregar nuestras propias extensiones, empezemos por la primera línea /etc/mime.types: en una ventana terminal metemos el comando “sudo nano /etc/mime.types” y podremos ver un encabezado parecido al siguiente:

###############################################################################
#
#  MIME media types and the extensions that represent them.
#
#  The format of this file is a media type on the left and zero or more
#  filename extensions on the right.  Programs using this file will map
#  files ending with those extensions to the associated type.
#
#  This file is part of the "mime-support" package.  Please report a bug using
#  the "reportbug" command of the "reportbug" package if you would like new
#  types or extensions to be added.
#
#  The reason that all types are managed by the mime-support package instead
#  allowing individual packages to install types in much the same way as they
#  add entries in to the mailcap file is so these types can be referenced by
#  other programs (such as a web server) even if the specific support package
#  for that type is not installed.
#
#  Users can add their own types if they wish by creating a ".mime.types"
#  file in their home directory.  Definitions included there will take
#  precedence over those listed here.
#
###############################################################################

En el último párrafo especifican que podremos crear nuestros propios tipos de ficheros por medio de un archivo en nuestro directorio personal de datos (“home”) y que tendrá preferencia aquella lista sobre esta. Pero si modificamos la lista principal los efectos tomarán para todos los usuarios en el ordenador donde estemos compartiendo archivos.

Es así que agregamos la siguiente línea, ya sea al final o buscáis el último grupo de líneas:

text/markdown                                   md
mime types text markdown md
mime types text markdown md

Previamente, por medio de nuestro editor de texto favoritos, nano, buscamos que la palabra “markdown” no esté registrado ni tampoco la extensión de archivo “md“. Luego le damos guardar y salir y relanzamos de nuevo nuestro servidor web escrito en Python para compartir archivos y solicitamos descargar de nuevo el archivo markdown del guion que le hicimos “fork” antes de resubirlo a GitHub. Nuestro navegador ahora nos mostrará el diálogo de descarga de una manera diferente y hasta nos propondrá editarlo con nuestro editor de texto con entrono gráfico favorito gedit, mirad:

Abriendo README.md
Abriendo README.md

Y claro, solicita abrirlo con gedit porque le estamos especificando que es un archivo de texto pero con una nueva clasificación “por nosotros propuesta”, tipo markdown.

Presentando ficheros markdown como HTML.

Para ensanchar nuestros conocimientos haremos un ejercicio interesante en cuanto a agregar funcionalidades a nuestro servidor web escrito en Python. La idea consiste en agregar una librería a nuestro Python y el cual permite convertir código markdown en HTML. Para ello debemos primero descargar el software necesario, tanto para Python 2.X como Python 3.X, mirad la imagen:

pip install markdown
pip install markdown

Una vez tengamos instalada la librería, corremos una terminal Python y hacemos unas cuantas pruebas y confirmar como convierte en código HTML lo que le pasemos en código markdown:

import markdown
import markdown
Python 3.5.2 (default, Nov 17 2016, 17:05:23) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import markdown
>>> print(markdown.markdown("* Lista "));
<ul>
<li>Lista </li>
</ul>
>>> print(markdown.markdown("# Título 1 "));
<h1>Título 1</h1>
>>>

Ya tenemos nuestro conversor; la idea que tenemos en mente es mostrar todo archivo con extensión “.md” en la carpeta que compartimos en ambos formatos pero con una diferencia: el enlace mostrado en el navegador en realidad NO es un fichero real en disco sino una “traducción” del fichero tipo markdown que sí existe en disco. Por supuesto nuestro navegador web al hacer click en uno de estos enlaces y recibir la cabecera de datos que recibe del servidor web Python se comportará de diferentes maneras: ante un fichero “.html” no ofrecerá guardar en disco sino directamente mostrarlo por pantalla. Esto que queremos hacer pareciera no tener sentido alguno pero si alguna vez llegáis a estudiar el lenguaje PHP entenderéis como un guión escrito en un lenguaje es convertido a un lenguaje de marcación como HTML. Cuesta un poco acostumbrarse a esta manera de trabajar en PHP pero una vez que uno le toma el gusto todo va sobre ruedas.

Ahora presentamos un código de prueba para exhibir un fichero markdown por pantalla en formato HTML, para ello también echaremos mano de la librería codecs que viene por defecto en las instalaciones de Python, veamos (recordad que tenemos abierta una ventana terminal en la carpeta que estamos compartiendo):

>>> import markdown
>>> import codecs
>>> archivo_md = codecs.open("README.md", mode="r", encoding="utf-8")
>>> texto = archivo_md.read()
>>> print(markdown.markdown(text);
<p><strong>Utilidad escrita por el sr. Elías Rodríguez Martín</strong></p>
<p><a href="https://linuxgnublog.org/es/compartir-archivos-facil-y-rapidamente-gracias-a-python/">Entrada hecha en Linux GNU Blog para compartir archivos con Python</a></p>
<p><em>Adición hecha por Jimmy Olano para soportar caracteres no ascii (utf-8) en os.chdir()</em></p>
<p><a href="http://www.ks7000.net.ve/2017/04/15/python-http-server/">Entrada hecha en KS7000 + WP para ampliar el alcance de dicho guion</a></p>
<p><a href="https://www.gnu.org/licenses/license-recommendations.html">Propongo tenga una licencia Apache que es adecuada para pequeños proyectos 8-)</a></p>

Por supuesto que hay algunos detalles que corregir: primero se debe colocar el encabezado del archivo HTML, luego el “cuerpo” que es el código anterior y al final las etiquetas de cierre del archivo HTML. Segundo se debe comprobar que el código sea “limpiado” y presentado correctamente ya que proviene de una fuente “no confiable”. Para ello recomiendan una herramienta llamada Bleach (Blanqueador) el cual se debe instalar via pip y no explicaremos más para no poner pesada esta entrada (más pesada, queremos decir).

Ya tenemos la idea de lo que vamos a hacer, ahora analizamos el código que vamos a modificar el cual podemos inspeccionar y descargar desde este enlace. En realidad son dos funciones que modificaremos: la función que muestra los ficheros en un archivo virtual en formato HTMl con un listado de los archivos a compartir y luego la otra función que entrega el ficero en sí mismo.

La primera función es llamada list_directory y la segunda es llamada do_GET y ambas pertenecen a la clase SimpleHTTPRequestHandler.

Fuentes consultadas:

En idioma castellano:

En idioma inglés:

ABC HunSpell

Hunspell tutorial

Repetimos que esta página no va de ciencias sociales, sin embargo somos fanáticos de la ortografía y en esta oportunidad os presentamos una herramienta para corregir nuestros escritos, que ya son bastantes, por demás. Ahora es el punto donde nos diréis “ea, tío,  ¿de qué váis si eso está integrado en ‘todos’ los programas?” Pues bueno preparaos a sorprenderos con los que os vamos a contar, ¡vamos a aprender a programar para corregir nuestra ortografía!

Introducción.

Ya tocamos el tema del idioma inglés anteriormente, su semántica y ortografía, un toque leve pues, nada profundo. Ahora os presentamos una herramienta llamada HunSpell para ayudarnos rápidamente a corregir archivos completos de una manera automatizada con dos grandes ventajas: se puede usar la línea de comandos (scripts bash) y con Python3.

Historia.

HunSpell deriva de MySpell.

MySpell.

MySpell fue desarrollado por Kevin Hendricks quien buscaba integrar varias herramientas de software libre para la suite de oficina Apache OpenOffice.org pero no fue sino con la ayuda de Kevin Atkinson (creador de Aspell -sucesor de Ispell- y Pspell) y un arduo trabajo en C++ que nace MySpell el cual soporta “Affix compression” el cual tiene una capital importancia para nosotros los programadores y ya veremos cómo cuando toquemos el tema con Python (Python3 en específico).

HunSpell.

¿Por qué crean HunSpell si ya existía MySpell? Porque este mundo es grande y ancho y para el idioma húngaro se necesitaba un corrector ortográfico que pudiera manejar UTF-8 y por ende los idiomas del mundo entero. Nosotros por estos lares siempre indicamos -y luchamos- para que esta codificación en bases de datos, lenguajes de programación y lenguajes de marcado sean presentados correctamente tanto por pantalla como por impresoras (aunque estas útlimas se están dejando de usar para ahorrar papel, tinta, dinero y recursos naturales). De allí proviene el “HUN-“del idioma húngaro, “Spell” es deletrear en inglés, de allí la palabra compuesta.

La última versión estable data del 2011 y al igual que su predecesor MySpell, HunSpell está escrito en lenguaje C++. En su repositorio bien reza “la librería de corrección ortográfica más popular” y al momento de escribir estas líneas tenía 7 horas de haber sido actualizado dicho repositorio ¡está más viva que nunca HunSpell!

Programas que utilizan HunSpell.

Y he aquí la sorpresa: HunSpell es el corrector ortográfico de fuente abierta para una gran cantidad de programas que usamos diariamente, por nombrar unos cuantos:

  • Apache OpenOfiice.
  • ¡LibreOffice!
  • Thunderbid.
  • Mozilla Firefox ¡desde el año 2005!
  • Google Chrome.
  • Google Chromium.
  • SeaMonkey.
  • WinShell.
  • Opera.

Lenguajes de programación y HunSpell.

Ya os dijimos que HunSpell está escrito en lenguaje C++ pero al menos hay dos lenguajes en los cuales podemos imprtar estas librerías. Uno es Python, en el cual estamos vivamente interesado gracias a su gran versatilidad y plataformas que lo soportan. El otro es el lenguaje R que ha tomado auge recientemente y que, por ahora, no tocaremos su uso. En nuestra sección de fuentes consultadas en idioma inglés os dejamos unos enlaces con ejemplos que nos parecieron bastantes sencillo por si queréis ahondar en ese tema.

Instalando HunSpell en Ubuntu.

Como en las distribuciones basadas en Debian, podemos utilizar apt para instalar por una ventana de comandos desde nuestros repositorios predeterminados. Para ello con una simple línea -con derechos de administrador root-, escribimos lo siguiente:

sudo apt-get install hunspell
apt get install hunspell
apt get install hunspell

Previo a este comando es recomendable usar apt-get update para sincronizar el catálogo de aplicaciones entre nuestro ordenador y los repositorios que tengamos predefinidos. Una vez hayamos instalado podemos consultar la versión que hayamos instalada:

hunspell -v
hunspell -v
hunspell -v

Como podéis constatar aparecen las atribuciones debidas hacia Ispell, László Németh, MySpell, Kevin Hendricks y openOffice.org: así es el software libre, se debe heredar la licencia y hacer econocimiento expreso a los autores a fin de evitar el patentado y que devenga en software  privativo.

Uso por línea de comandos de HunSpell.

ABC HunSpell
ABC HunSpell

Uso de Hunspell de manera interactiva.

Una vez tengamos instalado HunSpell podemos, sin más, abrir una ventana de comando y comenzar a usarlo. Aunque su uso principal es para revisar archivos o ficheros de texto, también podemos hacerlo interactivamente. Primero debemos conocer dónde estamos parados, conocer cuáles diccionarios tenemos instalados, y por ello usaremos el siguiente comando -el cual devuelve una extensa respuesta-:

hunspell -D

Este parámetro nos devolverá, esencialmente, tres secciones:

  1. Rutas de búsqueda: dependiendo de la cantidad de programas que tengamos instalados, cada uno de ellos -si utilizan MySpell o HunSpell- tienen sus propios diccionarios. Dada la filosofía del software libre, tenemos la opción de usarlos, descargar otros e incluso crear nuestros propios diccionarios -¡avanzado!-.
  2. Diccionarios disponibles: la “segunda” ubicación es la del propio HunSpell, generalmente está ubicada en la carpeta “/usr/share/hunspell” (esta ruta puede variar según vuestra distribución linux utilizada). En estas rutas también puede aparecer la ruta “/usr/share/myspell” así que si tenemos más opciones, pues mejor. Los diccionarios allí listados son simples ficheros cuyo nombre está compuesta por dos letras minúsculas, el guión bajo “_” y dos letras mayúsculas. Las primeras dos letras corresponden al idioma y las segundas dos letras corresponden a la distribución regional. Por ejemplo “en_GB” representa “english Great Britain”, “fr_FR” francés de Francia, “fr_CA” francés de Canadá y así sucesivamente. Para nuestro país -aunque lo identifican mal- nos corresponde “es_VE” osea español de Venezuela (cuando en realidad hablamos es castellano). Importante destacar que si queremos cargar cualquiera de los diccionarios en esta sección listada no es necesaria especificar su ruta ya que HunSpell sabe muy bien donde están ubicados.
  3. Diccionario cargado: dependiendo de la configuración regional que tengamos en nuestro sistema operativo, será cargado un diccionario de manera predeterminada, por lo cual, como dijimos, podemos usar sin más a HunSpell. Para nuestro caso tenemos el fichero “es_VE.dic” y su contraparte -ya veremos su uso- “es_VE.aff“.

Vamos, pues, a comenzar a usar a HunSpell. Ya sabemos cual diccionario tenemos cargado por defecto, pero acostumbremonos a penar internacionalmente, que es de cultura general saber o al menos tener nociones de varios idiomas modernos:

hunspell -d es_VE

Acá notamos que usamos el parámetro “-d” para cargar un diccionario que se encuentra en la ruta de HunSpell, por lo que no es necesario especificar su ubicación, HunSpell sabe ya donde está. A continucación presionamos intro o enter y escribimos la plabra que deseamos revisar y presionamos INTRO. Si la palabra está bien escrita (HunSpell la busca en el diccionario y consigue una conicidencia exacta) nos devolverá ya sea asterisco “*“, signo de suma “+” o un signo de resta ““. Por ahora nos conformamos en saber que la palabra es correcta. De estar mal escrita la palabra (no se consigue en el diccionario) HunSpell nos devuelve una lista de palabras aproximadas donde, generalmente, la primera que aparece es la correcta. Aquí es muy importante el archivo “es_VE.aff”, que utiliza el “affix compression” -fijaos la extensión del fichero-: hay palabras raíces y con prefijos, sufijos o ambos, encontramos su aproximación a la palabra correcta. Es decir, según unas reglas predeterminadas (palabras raíces, palabras derivadas) podemos corregir y lograr la ortografía.

Muchas veces devolverá pocas y a veces varias, veamos la imagen siguiente:

hunspell -d es_VE
hunspell -d es_VE

Notamos que si escribimos mal el nombre de nuestro país, HunSpell nos devuelve en primer lugar la aproximación más cercana y luego una segunda palabra que medio se le parece. Hasta aquí todo bien pero si escribimos la palabra “vurro” (sic) lo primero que nos devolverá es “curro” -palabra no muy común en Venezuela- y en segunda opción la buscada, el animal muy útil en las granjas aún hoy en día. Luego nos muestra palabras menos comunes aún. Es por ello que debemos aprender a crear o modificar nuestros propios diccionarios de acuerdo al uso más común que damos a nuestra habla cotidiana. Es evidente que este diccionario proviene de España ¡NO HAY PROBLEMA CON ESO! Allá se originó nuestro castellano, de la región abundante en castillos, Castilla, que era más fortificada y pudo someter militarmente al resto de la península ibérica e inclusó expulsó a los moros que por 600 años estuvieron allí construyendo ciudades y civilizando a la población con los últimos avances científicos de la época.

Pero seamos sinceros, las diferencias son evidentes entre el castellano de España y el castellano de Venezuela:

  • coche -> carro.
  • móvil -> celular (teléfono).
  • piso -> apartamento (departamento, vivienda).
  • ordenador -> computadora.
  • contadores -> medidores (electricidad).
  • Y paren ustedes de contar…

Uso de Hunspell sobre archivos de texto.

Cuando le indicamos a Hunspell que trabaje sobre un archivo de texto no es completamente automático su comportamiento, en cierto modo es interactivo con nosotros. Pero primero veamos el comando a utilizar:

hunspell -d es_VE archivo_de_texto.txt

Una vez comenzamos la tarea, Hunspell cambia sin preguntar las palabras mal escritas que encuentre en coincidencia en el diccionario especificado. Pero si no consigue una coincidencia clara nos pregunta a nosotros con las siguientes opciones:

  • Tecla “R”: reemplaza la palabra mal escrita completamente.
  • Tecla “Espacio”: Acepta la palabra solamente por esta vez.
  • Tecla “A”: Acepta la palabra para el resto de esta sesión.
  • Tecla “I”: Acepta la palabra, y el ingreso en su diccionario privado.
  • Tecla “U”: Acepta y añade la versión minúscula en el diccionario privado.
  • Tecla “S”: Pedir una raíz y una palabra modelo para almacenarlas en el diccionario personal.
  • Números: cuando son pocas las opciones uno puede pulsar un solo número pero a veces la lista es extensa y hay que pulsar dos, como por ejemplo doble cero.
Hunspell sobre un archivo de texto
Hunspell sobre un archivo de texto

Uso práctico de Hunspell.

Pues si ya ustedes son lectores habituales de este vuestro humilde blog -y sino pues ahora lo sabrán- somos terriblemente pragmáticos con el software, nos gusta, amamos y nos encanta darle uso práctico a las herramientas informáticas. Para este caso estamos usando el Hunspell con los archivos de texto generados con el reconocimiento óptico de caracteres, al cual le dedicamos una entrada correspondiente. A esas imágenes que capturamos con un aumento de 200% (zoom le dicen las personas que hablan inglés) y con ayuda de el maravilloso Shutter capturamos por párrafos y luego los corregimos de manera rápida con Hunspell.

Fuentes consultadas.

En idioma castellano.

En idioma inglés.

Python logo sin texto

Como trabajar con formatos de cadenas de texto en Python 3

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:

'¡Hola! ¿cómo están?'
"Nosotros bien, ¿y ustedes?"

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:

print('¡Hola! ¿cómo están?')
print("Nosotros bien, ¿y ustedes?")

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

  1. 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.
  2. Caracter separador, los dos puntos “:”.
  3. Caracter de relleno, cualquiera que se necesite (hicimos un ejemplo con un asterisco, el ejemplo de los cheques bancarios ¿lo recordáis?).
  4. Alineación (recordad el ejemplo de los países), abajo especificamos más.
  5. Signo positivo, negativo o cero (abajo aclaramos).
  6. En el caso de números en otros sistemas de numeración podemos usar “#” o “0”, de nuevo abajo explicamos mejor el asunto.
  7. Anchura del campo, un número entero de no muchas cifras ya que el ancho de pantalla generalmente es 80 caracteres en remembranza de los antiguos monitores monocromáticos.
  8. Opción de agrupamiento: aquí si que hay mucha tela que cortar, mirad el punto 8 que lo hacemos muy detalladamente.
  9. 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.
  10. 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
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
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).

Ejemplos de conversiones:

Ejemplos de conversión a otros sitemas de numeración con el comando format().
Ejemplos de conversión a otros sitemas de numeración con el comando format().

Fuentes consultadas:

En idioma inglés:

Python logo sin texto

Python 3.5.2 tutorial: funciones

Hoy día continuamos el aprendizaje: veremos las funciones en Python ¡adelante!

Python logo original
Python logo original

Introducción.

Esta es la segunda parte de nuestro tutorial sobre Python, específicamente la versión 3.5.2, para ser exactos. Para mayores detalles de compatibilidad, historia y, en fin, lo básico sobre este lenguaje, debéis leer primero nuestra entrega anterior. Pero si ya tenéis alguna experiencia, podéis comenzar aquí con funciones. Y si soís un profesional en esto, gracias por vuestra visita, vuestras correcciones las podéis hacer llegar vía Twitter a @ks7000 o realizar un comentario acá abajo al finalizar esta entrada. Pues bien, ¡manos a la obra!

Funciones en Python.

Clasificación de las funciones.

En la primera parte de nuestro tutorial hicimos una sencilla función, demasiado simple, para nuestro programa que_hubo_mundo.py, y ahora es bueno que veaíś el panorama completo de las funciones desde nuestro punto de vista.


Advertencia.
Advertencia.

Lo aquí expresado por nosotros es nuestra opinión, la forma y manera en que enfocamos el aprendizaje del lenguaje Pyhton. En la página web oficial podréis ver el tutorial completo -en inglés- y con todo detalle. Nuestra idea es que aprendáis lo básico y una vez estéis diestro o diestra en la materia, repaséis en Python.org y contrásteis la información. ¿Queréis continuar? ¡Vamos!


 Las funciones en Python las consideramos en el siguiente orden:

  • Funciones nativas o integradas: no se necesita acción alguna fuera de llamarlas y pasarles los argumentos, si es que exige alguno. Ejemplo fácil: la función print() la cual podemos llamarla y simplemente nos “devuelve” un retorno de carro por cónsola en pantalla. Y decimos que nos “devuelve” porque eso es lo que vemos que hace la función, porque en realidad no nos devuelve valor alguno, ni numérico ni de texto. ¿Recordáis la palabra clave “end” para print()? Ingresad en vuestra cónsola lo siguiente (le “pasamos” un argumento a print() especificamente un argumento con palabra clave): print(end=””) ¿Qué os parece?
  • Funciones que vienen con Python pero hay que declararlas primero para luego llamarlas: pues eso, vamos a traer un ejemplo práctico, cuando vosotros estáis trabajando en modo interactivo tal vez os gustaría poder “limpiar” la cónsola de todo lo que hayáis practicado. Pues bien, esta tarea descansa con el sistema operativo de vuestro ordenador, y en el caso de GNU/Linux se hace con el comando “clear” pero antes que intentéis probarla revisad primero el siguiente código, observad la palabra clave o comando import:
  • >>> import os
    >>> os.system(“clear”)
    0
    >>>
  • Eso es lo que veréis, más o menos. La pantalla se “limpiará” pero obervareís que aparece un cero y luego el prompt de Python interactivo. Por eso es que es una función, devuelve un valor, que en este caso es cero. Si queremos que ese cero no aparezca y veamos una prístina cónsola (solo el prompt, por su puesto) deberemos teclear algo como esto: k = os.system(“clear”). Al finalizar estos párrafos os colocamos una captura de pantalla para que veaís más detalles.
  • Funciones para Python que hay que descargarlas e importarlas: nosotros podemos publicar (como de hecho lo hicimos) nuestro archivo que_hubo_mundo.py el cual contiene la función hola() la cual solamente imprime el mensaje “¡Qué hubo mundo!”. Abrid una ventana terminal, listad los ficheros con el comando ls y si tenéis el archivo en cuestión en esa carpeta entonces abrid la terminal interactiva e ingresad lo siguiente: from que_hubo_mundo import hola y observad lo que pasa. ¡Genial! ¿o no? Pues bueno así podéis crear tus “librerías” e incluir el trabajo de otros, así, sin mayor parsimonia ni protocolo (punto 3 PEP20). El problema de esto es que si traemos el trabajo de otras personas hasta nuestro disco duro y dichos programadores publican una nueva versión (para mejor, se supone) deberemos descargar manualmente y verificar si funciona con nuestro programa -dos trabajos en uno-. Para evitar esto pasemos al siguiente punto.
  • Funciones para Python que hay que instalarlos por un procedimiento especial: Python.org mantiene siempre sus puertas abiertas a contribuciones, tanto monetarias como de programas y para ello mantiene en línea “the Python Package Index” (El Índice de Paquetes de Python) con el cual podemos instalar el trabajo de los demás y mantenernos actualizados si surgen nuevas versiones. El “procedimiento especial” consiste en instalar el programa pip el cual será el que haga el trabajo por nosotros (aunque probablemente ya lo tenéis instalado). A su vez, para instalar pip debemos descargar un archivo llamado get-pip.py desde un servidor seguro (HTTPS, “candado verde”) y ejecutarlo. Luego podremos usar directamente desde Python.org cualquier trabajo publicado. Aquí es importante acotar que se deben seguir normas y reglas para publicar y son de obligatorio cumplimiento si queremos llegar lejos en esto. Sabemos que si seguimos estudiando y trabajando duro algún día publicaremos algo allí pero si queréis probar cómo funciona (declarar, subir y luego bajar nuestra programación) Python ofrece un ambiente de prueba (le dicen “sandbox” en inglés ya que allá se acostumbra poner en los parques cajones de madera llenos de arena para que los niños y niñas desarrollen sus habilidades motoras sin dañar nada ni herirse): he aquí el enlace y “happy hacking!” como dicen allá 😉 .
os.system("clear")
os.system(“clear”)

Creación de una función con el comando pass.

La manera intencional de declarar una función que no haga nada pues tiene que darse con un comando que tampoco haga nada. Os presentamos el comando pass. Y eso es lo que hace, nada de nada. Mirad este código:

>>> def adios():
...     pass #Aquí colocaremos código a futuro
...
>>> adios()
>>>

Como veís, al invocar la función no realiza “nada”. Aquí vamos a explicar línea por línea:

  • Línea 1: definimos la función con la palabra reservada def seguido de un espacio y el nombre que hayamos escogido para la función. Luego colocamos par de paréntesis, uno que abre y otro que cierra, allí luego colocaremos los argumentos o datos que le queremos pasar a la función. Los dos puntos “:” indican que la línea no acaba allí, que hay más en líneas siguientes y al presionar intro observaréis que el prompt pasa a ser “…” y nos invita a escribir la segunda línea.
  • Línea 2: en la primera parte de este tutorial hablamos de configurar nuestro editor de textos para que al presionar la tecla TAB nos inserte espacios en vez del caracter tabulador. Ahora os rogamos, para cumplir con las normas de Python.org que lo configuréis exactamente a 4 espacios o que pulséis 4 espacios la primera vez y luego vuestro procesador indentará automáticamente cada vez que presionéis intro. Así que colocamos el comando pass (que no hace nada sino gastar 4 bytes en nuestro disco duro al estar guardado en un archivo). Luego colocamos un comentario sobre lo que pensamos hacer a futuro con dicha función.
  • Línea 3: una línea en blanco para poder presionar intro y volver al prompt interactivo. También debemos dejar una línea en blanco si estuvieramos escribiendo en un archivo, por razones de legibilidad y claridad en nuestro código.

Creación de una función útil en la vida real.

Muy bonito estudiar la teoría pero debemos de ser pragmáticos y ponerla a trabajar en nuestras vidas. Para ello vamos a realizar una función que dado un monto nos calcule el impuesto a las ventas que debemos pagar (Impuesto al Valor Agregado, I.V.A. o IVA). En nuestro país, Venezuela, todo eso está normado por la ley y si queréis revisarlo nosotros hemos publicado una entrada al respecto.

El cálculo es el siguiente: al monto (que llamaremos base gravada o base) le multiplicamos por la tasa de impuesto y la dividimos entre cien. A la fecha la tasa es de un 12% pero puede aumentar o disminuir con el paso del tiempo, pero en general dura meses y hasta años en un mismo valor. Asumiremos entonces que es 12% y pasamos a escribir la función:

>>> def impuesto(base):
...     print('Impuesto a pagar:', round(base*12/100,2))
... 
>>> impuesto(100)
Impuesto a pagar: 12
>>>

Rápidamente observamos, en la única linea de la función, como hemos utilizado la función print() , hemos separados con comas los argumentos para nos imprima un espacio en blanco entre ellos, y uno de los argumentos le hemos pasado una funcion round() a su vez con dos argumentos: el cálculo del impuesto en sí y el número 2 para indicarle que redondee el impuesto a dos decimales ya que así lo exige la ley (hay ciertos casos como el manejo de moneda extranjera o divisa -por ej.: €- y venta de hidrocarburos que, debidos a los altos montos, al multiplicar con solo 2 decimales trae mucha diferencia: solo en esos casos se exigen 4 decimales ¿por qué demonios explicamos esto? Más adelante veréis ).

Por supuesto, el comando pass lo eliminamos y el comentario también, pero no debemos dejarlo así, siempre debemos agregarle un texto explicativo, no solo para nosotros a futuro, que se nos pueden olvidar las cosas (¡vamos , a menos que seas Rainman!) sino para enriquecer a la comunidad de software libre, debemos compartir nuestras creaciones (y cobrar por el servicio, claro está, el que trabaja se gana su pan diario). Si con esos argumentos no os hemos convencido aquí va un tercero que es necesario: Python.org tiene algo llamado docustring que fue diseñado para que se puedan construir manuales a partir de un formato especial que nosotros agreguemos a nuestras funciones. ¿Cómo funciona esto? Solamente os decimos que debemos seguir estudiando cómo escribir funciones y en su oportunidad volveremos a tocar el tema.

Baste entonces con escribir nuestro comentario encerrado en un  entrecomillado triple para cumplir con Python.org y sus normas, veamos

>>> def impuesto(base):
...     ''' Cálculo del IVA a un monto dado '''
...     print('Impuesto a pagar:',round(base*12/100,2))
... 
>>>

¿A que es más lúcida nuestra función? Pero ojo, ahora vamos a llamar la función impuesto() tal cual, sin ningún monto y veremos un hermoso error, al traste con nuestra emoción, pero la computadora está en lo correcto, y si nosotros hemos fallado ¿qué esperamos entonces de nuestros futuros usuarios de nuestros programas?

Pues he aquí que vamos a mejorar nuestra función, la versión 0.1, de esta manera:

>>> def impuesto(base=0):
...     ''' Cálculo del IVA a un monto dado V. 0.1 '''
...     if base==0:
...         print('Introduzca un monto mayor que cero.')
...     else:
...         print('Impuesto a pagar:', round(base*12/100,2))
>>>

Agregamos un valor por defecto así que si al llamar a la función no le pasamos argumento entonces toma un valor de cero y si el valor es cero imprime un mensaje invitando a hacerlo. Hemos  convertido una función con argumento obligatorio en una que no lo es. Esto tiene ventajas y desventajas: si no le ponemos valor por defecto nos obligamos nosotros mismos a ser mejores programadores. Pero como le pusimos valor por defecto es recomendable que el programa avise que se le está pasando, así sea verdad o no, un valor cero, por lo tanto el impuesto sería cero y la función no tendría razón de ser. Si nos equivocaramos programando y en algún punto llamamos a la función sin argumento con el tiempo recibiremos una hermosa llamada telefónica de nuestros clientes diciendo algo como “metemos el monto de la base y el programa dice que introduzcamos un valor mayor que cero“.

Así nuestra función sigue evolucionando: volvemos a colocarla para que sea obligatoria la base como argumento PERO dejamos el mensaje y lo mejoramos para manejar valores negativos, la versión 0.2 quedaría así:

>>> def impuesto(base):
...     ''' Cálculo del IVA a un monto dado Ver. 0.2 '''
...     if base<=0:
...         print('Introduzca un monto mayor que cero.')
...     else:
...         print('Impuesto a pagar:', round(base*12/100,2))
>>>

Funciones con argumentos con palabras clave.

Os dijimos que el monto de la tasa de impuesto dura meses y hasta años con el mismo valor, pero eventualmente cambia. Por ello vamos a pensar en el futuro y modifiquemos otra vez, sería la versión 0.3:

>>> def impuesto(base, tasa=12):
...     ''' Cálculo del IVA a un monto dado Ver. 0.3 '''
...     if base<=0:
...         print('Introduzca un monto mayor que cero.')
...     else:
...         n_tasa = input('Introduzca tasa (12%)')
...         if len(n_tasa) > 0:
...             tasa=float(n_tasa)
...         print('Impuesto a pagar: Bs.', round(base*tasa/100,2))
...
>>>

Aquí le damos la oportunidad al usuario de modificar la tasa si ha cambiado, de lo contrario pulsa intro y la tasa seguirá con su valor por defecto de un 12% que le colocamos al declarar la función. La función input() devuelve una cadena de texto: si el usuario escribió algo la longitud de la cadena es mayor a cero y la función len() nos lo dirá así, por otra parte la función float() convertirá la cadena de texto a un número de doble precisión, capaz de manejar decimales.

Pasaremos por alto que el usuario introduzca un valor no numérico, ya llegará el momento de mejorar nuestro código. Por ahora nos interesa seguir aprendiendo. Así como modificamos nuestra función nos permite para la tasa desde la llamada a la función, por ello es lo correcto mostrar dicho monto para que la función input() enseñe la tasa que va a aplicar, podemos modificar la línea de la siguiente manera:

...         n_tasa = input('Introduzca tasa ('+str(tasa)'%)')

Para llamar a la función escribiremos impuesto(100 , 15) para indicarle a la función, por lógica, que a un monto de Bs. 100 le calcule el 15% de impuesto. Pero con Python tenemos varias maneras de llamar a la función y todas producirán el mismo resultado: Bs. 15 -si simplemente presionamos intro cuando verifica la tasa-, probadlo en vuestras cónsolas por favor:

>>> impuesto(100 , 15 )
Impuesto a pagar: Bs. 15.0
>>> impuesto(base=100 , tasa=15)
Impuesto a pagar: Bs. 15.0
>>> impuesto(tasa=15, base=100)
Impuesto a pagar: Bs. 15.0
>>> impuesto(tasa=15, 100)
  impuesto(tasa=15 , 100)
                    ^
SyntaxError: non-keyword arg after keyword arg

En la última entrada nos arroja error porque no estamos pasando el valor con su palabra clave, Python espera que el primer argumento sea obligatorio y no importa si solo tiene 2 argumentos (con una palabra clave pues por descarte el otro argumento es el “restante”), es decir, si vamos a cambiar el orden de los argumentos siempre debemos pasarle la palabra clave para indicarle a donde va cada valor. De otra manera, pues nos aprendemos de memoria el orden exacto de los argumentos de cada función. Si tenemos un editor que nos ayude a escribir las funciones, bien, por medio de tooltips nos dirán el nombre y tipo de variable que espera cada función mientras estamos escribiendo un comando al programar. Pero en GNU/Linux se necesitan programas pequeños que hagan cosas pequeñas y las hagan bien. Previendo la programación a futuro, rumbo a la inteligencia artificial, Python nos ofrece algo muy interesante. Empecemos por docustring ¿lo recordáis?

Anotaciones en funciones.

Podemos ‘preguntarle’ a la función para qué sirve por medio de su docustring simplemente ingresando lo siguiente:

>>> print(impuesto.__doc__)
 Cálculo del IVA a un monto dado V. 0.3

Ya vamos viendo la utilidad de documentar a medida que programamos; también tenemos la posibilidad de “saber” cuántos argumentos recibe la función sin haberle pasado un solo dato, lo que nos prepara para el poder usarla (si es que otra persona la escribió o es lo que nosotros escribimos para otras personas). Para ello debemos agregar a nuestra función la siguiente notación “ -> float: ” ya que así definimos qué tipo de dato espera entregar y recibir la función (eso disciplina nuestras tácticas de programación, pasandole a la función el tipo correcto de dato):

>>> def impuesto(base: float, tasa: float =12) -> float:
...     ''' Cálculo del IVA a un monto dado Ver. 0.3 '''
...     if base<=0:
...         print('Introduzca un monto mayor que cero.')
...     else:
...         n_tasa = input('Introduzca tasa (12%)')
...     if len(n_tasa) > 0:
...         tasa=float(n_tasa)
...     print('Impuesto a pagar: Bs.', round(base*tasa/100,2))
...
>>>print(impuesto.__annotations__)
{'base': <class 'float'>, 'return': <class 'float'>, 'tasa': <class 'float'>}

¿Observáis el formato como devuelve las definiciones de los argumentos? Vamos a analizarlos y para ello lo colocaremos en varias líneas de manera indentada:

{
    'base': <class 'float'>,
    'return': <class 'float'>,
    'tasa': <class 'float'>
}

La primera y última línea encierra la definición por medio de corchetes de apertura y cierra. La segunda línea indica que un argumento tiene como nombre clave la palabra ‘base’ y debe ser un número flotante.  La tercera línea “return’ indica lo que devuelve la función, una variable numérica flotante (la cual habremos redondeado a dos decimales, como explicamos). No especifica nombre pues ya sabemos cómo se llama la función. La cuarta línea también indica que debe recibir un argumento llamado ‘tasa’ y debe ser númerica de tipo flotante. Notarán que las nombran entre corchetes angulares como “class” o clase en castellano: si nosotros hiciéramos nuestras propias clases podremos referirnos a ellas en cualquier momento. Hay clases definidas de antamano en Python: ‘float’ es una variables numérica que admite hasta 16 decimales (en su momento veremos los tipos de datos disponibles en Python).

Las anotaciones de funciones son totalmente opcionales y están definidas en PEP 484 y son de reciente adaptación, año 2015, aunque tienen su base en otras PEP, aparte de ser compatibles, en cierta forma.

Palabra clave “return“.

Lo próximo que vamos a realizar es transformar completamente nuestro código a una verdadera función con el comando return, veamos:

>>> def impuesto(base: float, tasa: float =12, decimales: int =2) -> float:
...     ''' Cálculo del IVA a un monto dado Ver. 0.4'''
...     monto=round(base*tasa/100, decimales)
...     return monto
... 
>>> valor_impuesto=impuesto(100)
>>> print(valor_impuesto)
12.0

Lo que hicimos fue agregarle un argumento adicional, opcional, con la palabra clave “decimales” y que por defecto es 2 (por ley de nuestra República) y la “tasa” de impuesto que por ahora es 12 pero que le podemos pasar a la función un valor diferente sea el caso. También utilizamos una variable llamada “valor_impuesto” donde guardamos el valor devuetlo para la función y a continuación lo imprimimos por pantalla; pero esta variable la podemos usar donde y como la necesitemos. Así podemos practicar de varias maneras el por palabras claves, veamos, que la práctica hace al maestro (recordemos que estamos en modo interactivo y simplemente al llamar la función se imprime automáticamente por pantalla, pero si la guardamos en un archivo .py debemos guardar en una variable para luego imprimirla, tal como codificamos, de lo contrario no tendremos salida por pantalla):

>>> impuesto(20)
2.4
>>> impuesto(100, tasa=7)
7.0
>>> impuesto(100, tasa=15, decimales=4)
15.0
>>> impuesto(1477, tasa=17, decimales=4)
251.09
>>> impuesto(1970, tasa=17.89, decimales=4)
352.433
>>> impuesto(1341, decimales=4, tasa=17.89)
239.9049

Lo que siempre debemos hacer es “pasarle” la base, y luego por medio de palabras claves, sin importar el orden, los otros dos argumentos, de lo contrario arroja error (o excepción que es el nombre correcto). Si queremos pasarle argumentos, sin importar el orden, debemos colocarle palabras claves a todos los argumentos, de la siguiente manera:

>>> impuesto(decimales=2, tasa=10, base = 1500)
150.0
>>> impuesto(decimales=2, tasa=10)
Traceback (most recent call last):
 File "/usr/lib/x86_64-linux-gnu/gedit/plugins/pythonconsole/console.py", line 378, in __run
 r = eval(command, self.namespace, self.namespace)
 File "<string>", line 1, in <module>
TypeError: impuesto() missing 1 required positional argument: 'base'

Sin embargo, si le pasamos solamente los decimales y la tasa y no le pasamos la base, arroja error, porque ese debe ser un argumento obligatorio , ya que tasa y decimales le colocamos valores por defecto y así los convertimos en argumentos opcionales.

Uso de variables para pasarlas a las funciones.

Veremos algo que es facilmente deducible, pero lo complicaremos un poco con los valores por defecto en los argumentos opcionales de una función. Nos explicamos: podemos guardar en variables los valores y luego pasarlos a la función:

>>> miMonto=450
>>> miTasa=10
>>> misDecimales=3
>>> miImpuesto=impuesto(miMonto, miTasa, misDecimales)
>>> print(miImpuesto)
45.0
>>> miImpuesto=impuesto(tasa=miTasa, base=miMonto, decimales=misDecimales)
>>> print(miImpuesto)
45.0

Esto no necesita mayor explicación: es simplemente pasarle los valores por medio de variables. Lo que es más avanzado es pasarle los valores por defecto a la función por medio de variables declaradas antes de definir la función y dichas variables luego pueden cambiar su valor pero la función quedará definida con los valores dados exactamente antes de haber definido la función. Podemos así redefenir la función y luego explicaremos su utilidad:

valor_tasa=15
def impuesto(base: float, tasa: float =valor_tasa, decimales: int =2) -> float:
    ''' Cálculo del IVA a un monto dado Ver. 0.4'''
    monto=round(base*tasa/100,decimales)
    return monto

valor_tasa=16

Aunque luego cambiemos la variable valor_tasa a 16, la función impuesto() quedará en un valor de  por defecto de 15 en la tasa a lo largo de todo el programa.

¿Recordáis que os dijimos que la tasa de impuesto puede variar, según las necesidades fiscales del país? Pues bueno, nuestra función se puede preparar a futuro (por ejemplo, realizamos una sencilla aplicación de facturación y manejo de inventario) si escribimos nuestras funciones de esta manera, las podemos a volver a recompilar rápidamente si al módulo principal le cambiamos el valor. 

Funciones con argumentos arbitrarios.

Podemos definir una función que acepte cualquier argumento, habiendo ya estudiado anteriormente la flexibilidad que denota Python. Son pocos caracteres que la componen, pero no se dejen engañar: es una función compleja y difícil de asimilar, muy genérica, y es difícil darle un uso práctico:

>>> def mi_funcion(*argumentos):
...     for mi_arg in argumentos:
...         print(mi_arg)
... 
>>> mi_funcion(20)
20
>>> mi_funcion()
>>> mi_funcion(20,"prueba")
20
prueba
>>> mi_funcion(20, "prueba", 17, 20, "otra prueba")
20
prueba
17
20
otra prueba
>>>

Funciones con argumentos arbitrarios pero con palabras claves.

Para “declarar” argumentos arbitrarios en una función solamente tenemos que precederla con un asterisco y así Python “sabrá” agruparlas en un “diccionario” lo cual nos permite listarlas (y trabajar) dichos argumentos (que serán una simple variable más dentro de la función). Pero como mejor es explícito que implícito, Python nos permite pasar argumentos con palabras claves (y todas deben llevar su palabra clave o producirá una “errror” o excepción). Tomemos de nuevo nuestra función de cálculo de IVA con una base dada

>>> def impuesto(**argumentos) -> float:
...     ''' Funciones con argumentos y palabras claves '''
...     monto=0
...     base = 0
...     tasa = 12
...     decimales = 2
...     for arg_clav in argumentos.keys():
...         if arg_clav=='base':
...             base=float(argumentos[arg_clav])
...         if arg_clav=='tasa':
...             tasa=float(argumentos[arg_clav])
...         if arg_clav=='decimales':
...             decimales=int(argumentos[arg_clav])
...     monto=round(base*tasa/100,decimales)
...     return monto
...
>>> monto_iva=impuesto(decimales=2, base=500, tasa=7)
>>> print(monto_iva)
35.0
>>> print(impuesto.__annotations__)
{'return': <class 'float'>}
>>> print(impuesto.__doc__)
 Funciones con argumentos y palabras claves 
>>> 

Ya nuestras funciones van ganando complejidad y cada vez es más difícil escribirlas en modo interactivo, creemos que ya vale la pena escribirlas en un archivo por medio de nuestro editor de texto favorito, pronto daremos unas recomendaciones al respecto y unas normativas de Python. Mientras, veámos que sucede en este último ejemplo:

  • Línea 1: declaramos la función en sí con metadatos incluídos.
  • Línea 2: le establecemos su docustring.
  • Líneas 3 a 6: incializamos las variables con su valor pro defecto, si lo tuviera.
  • Línea 7: llamamos a un ciclo for para enumerar todos los argumentos con palabra claves “pasadas” a la función.
  • Líneas 8 a 13: por medio de sentencias condicionales verificamos si todos los elementos de la fórmula del cálculo del impuesto están presentes, si faltare alguno pues tomaría su valor por defecto.
  • Línea 14: realiza el cálculo en sí.
  • Línea 15: devuelve el resultado hacia fuera de la función.
  • Demás líneas: anotaciones de la función y su docustring. Obsérvese que en las anotaciones solo nos indica que es una función que solo devuelve un valor numérico flotante, sin ninguna información de los argumentos necesarios para que “haga su trabajo”

¿Qué utilidad tiene esto, declarar una función tan extraña que acepte cualquier cantidad de argumentos con palabras clave? Cuando veamos los diccionarios en mayor detalle se nos ocurrirán unos cuantos usos útiles, en este punto vamos a probar algo muy sencillo:

>>> diccionario = { "base" : 277.25 , "tasa" : 19 , "decimales": 3 }
>>> impuesto(**diccionario)
52.678
>>>

Fíjense que las palabras claves deben estar encerradas entre comillas, y que debemos colocarle dos asteriscos al llamar a la función, así estemos declarando la función con **argumentos, esto es lo más curioso del asunto y nos devuelve al principio: cualquier función acepta los argumentos con palabras claves si le colocamos ** al llamar la función con un diccionario ¿complejo, cierto? Y apenas estamos comenzando 😉 .

Funciones “clonadas”.

Python nos permite tomar las funciones que importemos y “cambiarles” el nombre. Así nuestra función impuesto() que escribimos, tal vez sea para nosotros memorizarla de otra manera, por ejemplo impt(), pues solo debemos escribir impt = impuesto  y pasarle los valores con el diccionario que hicimos anteriormente, he aquí:

>>> diccionario = { "base" : 277.25 , "tasa" : 17 , "decimales": 4 }
>>> impt = impuesto
>>> impt(**diccionario)
47.1325
>>>

Función abreviada lambda.

A veces necesitamos usar una función una sola vez (cosa rara) y codificarlo en 3 líneas como mínimo (aparte de ir en una sección aparte de nuestro proyecto dedicada a las funciones) pues que nos lleva tiempo y espacio. Para ello echamos mano de la ¿función de funciones? el comando lambda. Este comando nos permite aplicar, por ejemplo, un cálculo como el nuestro, que es sumamente sencillo, practiquemos:

>>> (lambda base : round(base*12/100,2))(100)
12.0

Como vemos no le colocamos nombre a la función, solo le indicamos que devuelva la misma base con el cálculo del impuesto (es todo lo que encierra el primer par de paréntesis de izquierda a derecha) y en el segundo par de paréntesis le pasamos el argumento con la base a calcular. Tal vez sea complicado de entender así que pasamos a definir la función y “clonarla” con un nombre:

>>> impuesto=lambda base : round(base*12/100,2)
>>> impuesto(1000)
120.0
>>> impuesto(7)
0.84
>>> impuesto(70)
8.4
>>> impuesto(100)
12.0
>>>

Acá vemos que es una manera de definir rápidamente una función. Si queremos colocarle los argumentos completos la podemos definir de la siguiente manera:

>>> impuesto=lambda base, tasa, decimales: round(base*tasa/100,decimales)
>>> impuesto(100,12,2)
12.0
>>> impuesto(543,17,4)
92.31
>>>

Evidentemente que los argumentos son posicionales, si queremos agregarles palabras claves… pues para eso definimos la función completa como ya lo hemos hecho. Consideramos útil a este comando/función dentro de otra función declarada para que se circunscriba dentro de esa función solamente, pero vosotros juzgad y dadle algún otro uso novedoso, ¡a practicar!

Estilo al codificar.

En PEP 8 se especifica cómo codificar nuestras funciones y para que queden correctas deben cumplir con las suguientes especificaciones:

  • Se deben indentar con 4 espacios y sin tabuladores (ver al final nuestras recomendaciones).
  • Las líneas no deben superar los 79 caracteres.
  • Se deben usar líneas en blanco entre funciones y clases.
  • Cuando sea posible, coloque los comentarios en la misma línea que describe.
  • Se debe usar docustring (¿deberíamos traducirlo como ‘docutexto‘?).
  • Se deben usar espacios entre los operadores y después de las comas pero no dentro de los paréntesis, ejemplo: a = f(1,2) + g(3,4) .
  • En el caso de las funciones y métodos se deben separar las palabras con guión bajo, por ejemplo impuesto_iva() , impuesto_sobre_renta() ; las clases se deben nombrar en estilo jorobas de camello: TasaDeImpuesto.
  • En el caso de que nuestro código sea para uso internacional, deberemos usar solamente caracteres básicos. Evidentemente que nuestro idioma utiliza muchos caracteres extendidos en UTF-8, así que todo dependen hacia adonde va dirigido.

Uso de gedit con python.

Por mucho nuestro editor de texto favorito es gedit debido a que tiene ciertas características deseables para programar pequeños proyectos de código.

Las capturas de pantalla aquí mostradas corresponde a gedit 3.10.4:

gedit version 3.10.4
gedit version 3.10.4

Al ejecutarlo lo primero que debemos activar es la barra de herramientas y la barra de estado en el menú desplegable “Ver”:

gedit barra de herramientas y barra de estado
gedit barra de herramientas y barra de estado

Al marcar estas dos opciones podremos ver en la aprte superios los comandos más usados como: documento nuevo, abrir documento, guardar, imprimir, etcétera. A pesar que todos estos comandos tiene sus atajos de teclados, los íconos de los botones tienen un efecto muy intuitivo. La parte más importante es la parte inferior la cual permite escoger qué lenguaje estamos programando y justo al lado la cantidad de espacios a insertar en el indentado automático y al presionar la tecla tabulador inserte espacios en vez de la caracter tabulador en sí, veamos:

gedit ventana principal
gedit ventana principal
gedit lenguajes soportados
gedit lenguajes soportados
gedit cantidad de espacios a usar con la tecla TAB
gedit cantidad de espacios a usar con la tecla TAB

También podemos guardar nuestras perferencias para la próxima vez que ejecutemos gedit, para hacerlo vamos al menu desplegable “Editar” -> “Preferencias” y marcamos las siguientes opciones, todo según las normas de estilo de Python:

Preferencias de gedit
Preferencias de gedit
  • Mostrar los números de línea nos sirve para hallar rápidamente dónde debemos corregir cuando el depurador Python nos indica algún fallo de sintaxis.
  • Al mostrar el margen derecho con una pequeña marca de agua nos ayuda a no sobrepasar los 79 caracteres.
  • Si activamos el ajuste de texto, así no sobrepasamos los 79 caracteres por línea pero cambiamos el tamaño de nuestra ventana, gedit nos colocará las líneas que no quepan en la ventan en múltiples líneas pero respetando la numeración para indicarnos que es una sola línea sobre la que estamos trabajando. La opción de no dividir palabras nos permite los comandos completos, sin divisiones.
  • La opción de resaltar la línea actual nos permite enfocarnos en la linea sobre la cual estamos editando y si es multilínea (ve punto anterior) nos  ofrece un mejor panorama.
  • Por último resaltar parejas de corchetes nos permite anidar funciones y sus argumentos son más fáciles de visualizar.
gedit activar ajuste de texto
gedit activar ajuste de texto

Una vez hayamos fijado nuestras preferencias (la cuales se aplican en tiempo real, así que muestran como se ve nuestro archivo de una vez) también podemos activar los complementos que nos serán muy útiles:

 

gedit complementos: completado de palabras, completar paréntesis y cónsola python.
gedit complementos: completado de palabras, completar paréntesis y cónsola python.
  • El completado de palabras “memoriza” los comandos que hayamos escrito y al nosotros volver a escribirlos el complemento lo sugiere por medio de un pequeño menú emergente. Recomendamos hacer click en “preferences” y marcar interactivo y un mínimo de 3 letras por palabra.
  • Completar paréntesis, corchetes rectos y llaves evita el error común de no hacerles el cierre. No funciona con los corchetes angulares “<” y “>”.
  • Por último la cónsola python: al activarla podemos pulsar CTRL+F9 y una subventana en la parte inferior nos abre una cónsolar interactiva con práticamente las mismas características de la cónsola python. Podemos volver a la edición del documento con la tecla ESC y volver a abrir la cónsola python con CONTROL+F9.
gedit con la cónsola python activada
gedit con la cónsola python activada

 

Fuentes consultadas:

En idioma castellano:

Enlaces en idioma inglés:

Python logo sin texto

Python 3.5.2 tutorial

Introducción.

Python logo original
Python logo original

En una publicación anterior tratamos el tema de generar códigos QR con lenguaje Python y hasta escribimos un sencillo cliente FTP para sincronizar los archivos de una carpeta local versus una carpeta remota. He aquí que entonces que necesitamos un tutorial sobre cómo funciona el lenguaje Python, un lenguaje versátil multiplataforma. Específicamente estaremos tratando la versión 3.5.2 porque consideramos que es el futuro y tiene varios años ya de existencia entre nosotros, dado el caso que valga la pena referirnos a alguna versión 2.X lo especificaremos pero la idea es ser lo más sencillo posible con abundancia de ejemplos y la menor teoría posible.

Historia del lenguaje Python.

A finales de los años 1980 el Doctor Guido Van Rossum creó el lenguaje Python para que sustituyera al lenguaje ABC en el Centro para las Matemáticas y la Informática en los Países Bajos. Su nombre proviene en honor del famoso grupo humorístico “Monty Python’s Flying Circus“. En un principio lo hizo por pura afición, y tiene su “hégira” en diciembre de 1989, mientras estaba de vacaciones. Hasta el día de hoy continúa trabajando en ello, ya que se convirtió en “benevolente dictador de por vida” (aún sigue al frente del desarrollo y es líder de la comunidad de seguidores) .

  • En febrero de 1991 publica la versión 1.0
  • En octubre de 2000 publica la versión 2.0 con soporte a caracteres Unicode, entre otros aspectos importantes.
  • En diciembre de 2008 sale la versión 3 o Python 3K la cual es incompatible con las versiones anteriores (os advierto no creaís a pie juntillas dicha aseveración, aprendamos y con el tiempo sopesaréis por vosotros mismos).

Diferencias entre la versión 2 y la versión 3.

Actualizado el viernes 26 de mayo de 2017.

Las versiones 3 en adelante vinieron a suplir una serie de necesidades del mundo actual. Si bien del del 2008 al 2016 han transcurrido más de 8 años -y ese tiempo en computación es una eternidad– y teniendo en cuenta que el único cambio que nos gusta a los seres humanos es el cambio de pañal, aún hoy en día no se ha podido migrar completamente todas las utilerías populares en Python. A grosso modo existen unas 360 y se han migrado unas 339, así que el progreso es de 94% pero ¡ojo! ésas son las librerías más usadas no necesariamente quiere decir que lo que ya estaba escrito migre automáticamente y esto debido a que hay serias incompatibilidades entre las versions 2 y 3.

La estrategia de migración de los desarrolladores de Python ha sido muy acertada: en julio del año 2010 (dos años después de haber lanzado la versión 3.0) emitieron una versión que marca un punto de inflexión en las versiones 2.x, la versión 2.7.0 (al momento de escribir estas líneas vamos por la versión 2.7.13).

El objetivo es que para el año 2020 se dejarán, esta vez definitivamente, de dar soporte y actualizaciones a todas las versiones 2.x y en estos tres años que faltan ir dando la oportunidad de que las aplicaciones escritas migren a la versión 3.x -“lo único constante es el cambio” así reza el refrán popular-.

De hecho nosotros aún tenemos instalada la versión 2.7.12 junto con la 3.5.2 (y Debian 8.8 aún trae ambas versiones, la 2.7.9 y la 3.4.2) debido a que colaboramos con algunos proyectos que están en versión 2.7.x: nuestra recomendación, de plano, si vais a comenzar un proyecto nuevo en Python es hacerlo de una vez en la última versión. Si por el contrario mantenéis alguna aplicación, pues ir migrando poco a poco teniendo en cuenta las principales diferencias, por nombrar solo algunas:

  • Print: en la versión 2.x es un comando y en la versión 3.x es una función, por lo que hay que escribirla entre paréntesis. Una ventaja de la nueva versión es que tiene soporte para caracteres Unicode por lo que ya no es necesario anteceder la letra “u” antes de lo que queremos imprimir para indicarle que lo haga en Unicode. Agregad solamente los paréntesis que aún la letra “u” la sigue soportando y no genera problema (lo probamos con la versión 3.5.2).
  • Otras funciones que ahora necesitan paréntesis: tales como, sin mencionar todas, exec, raise, except, (pueden necesitar, además de los apréntesis, cambios en la sintaxis).
  • División con enteros: esto de los tipos de variable es un poco largo de explicar pero fundamentalmente al dividir dos números enteros el resutlado es un número entero, para que conserve el mismo comportamiento en vuestros programas debeis sustituir “/” por “//“. En otros lenguajes para hacer una división que solo devuelva el cociente se utiliza la barra invertida “\” pero por la fuerte tendencia del lenguaje C en Python, dicho caracter tiene un comportamiento totalmente diferente ya que solo trabaja con caracteres, no con valores numéricos. Si se desea utilizar dichos operadores “//”” en Python 2 primero debemos declarar lo siguiente: from __future__ import division.

La transición de Python 2 a Python 3 es muchísimo más profunda de lo que expresamos aquí pero nuestra intención es colocarla como simple referencia en vuestras mentes, después del año 2020 solo Python 3, esperemos a ver.

Instalación de Python en nuestro ordenador.

Instalar Python en nuestro ordenador pasa por ejecutar en una ventana terminal, con los derechos suficientes, los siguientes comandos:

sudo apt-get update
sudo apt-get install python3
  • La primera línea actualiza nuestros repositorio de aplicaciones contra el respositorio remoto seleccionado, ya sea Debian o algún GNU/Linux derivado: Ubuntu, Canaima, Linex, etc.
  • La segunda línea descarga e instala en sí al Python3. Si queréis tener instalada también la versión 2 simplemente escribidlo sin el número tres. Podéis tener ambas instaladas pero debéis invocarlos (por supuesto) de diferente manera, ¿problema? ni tanto excepto si queréis ejecutar guiones -scripts- automatizados y allí si que cuenta cómo hayaís programado: para la versión 2.X o 3.X. Ojito pues, con esto.
  • Para instalarlo en otros sistemas operativos, acudíd al enlace web donde catalogan las descargas; hay múltiples versiones, escoged la adecuada. Recordad que lo que programemos os servirá en múltiples plataformas con mínimos cambios necesarios debido al hardware, más que todo, es el que hace la diferencia.

¿Para qué sirve el lenguaje Python?

Pregunta obvia, podemos ponerlo en contexto con unos cuantos ejemplos:

  • El lenguaje de marcado HTML sirve para realizar páginas web como esta que vosotros estáis leyendo, formato de títulos, párrafos, etc.
  • El lenguaje PHP sirve para generar páginas web dinámicas, incluso buscando contenido en bases de datos MySQL, PostgreSQL o MariaDB: es el caso de WordPress, la herramienta que permite la existencia de este nuestro humilde blog.
  • Mientras que los dos anteriores se ejecutan en el servidor web, el lenguaje JavaScript se ejecuta en vuestras computadoras, es decir, del lado del cliente, para ciertas funciones locales, como el tipo de navegador web e incluso sistema operativo.
  • Otros lenguajes especializados como el Pascal se especializan en tratamiento de números en el campo de las matemáticas, por eso tienen su nicho, por ejemplo, en las facultades de ingeniería, donde lo conocimos y aprendimos.
  • Existe el lenguaje C y sus variantes (C++, C#, etcétera) los cuales con sus seguidores y detractores (Linus Torvalds ama C y odia C#) siempre se ubican en el desarrollo de sistemas operativos como GNU/Linux. El lenguaje C poco le sobra por encima del lenguaje ASSEMBLER el cual es utilizado por el firmware de dispositivos primitivos -poca potencia de cálculo- y que no se pueden dar el lujo de programarlos con un lenguaje de alto nivel (el que podemos entender nosotros los humanos). El lenguaje C es, por tanto, considerado el padre de los demás lenguajes y marca un hito de separación entre la máquina y los humanos, pero no quiere decir que sea fácil de aprender, se dice que con el lenguaje C hay que “reinventar la rueda” cada vez que se inicia un proyecto de software. Para evitar esto pues C++ y C# tienen integradas sus propias librerías y asistentes que facilitan enormemente la tarea, aunque aún sigue siendo una tarea larga y a veces compleja.
  • Pues bien, he aquí que el Python nace con la potencia del lenguaje C pero con múltiples módulos escritos por sus seguidores bajo la filosofía de software libre (su licencia de uso ha cambiado a lo largo de los años) los cuales están así al alcance de todos nosotros para realizar numerosas tareas en diversos sistemas operativos, con la sencillez de un archivo de procesos por lotes; pero tal vez sea esto lo que hace la falsa impresión a sus detractores: subestiman al lenguaje python dada su sencillez.

Así que vamos a numerar las ventajas que posee el lenguaje Python (de sus desventajas hablaremos en otra entrada, en su oportunidad):

  1. Aparte de sus funciones integradas, nativas, hay una enorme variedad de programas que funcionan como módulos, esto es posible porque con el tiempo ha alcanzado su masa críticas de usuarios.
  2. Es rápido y en unas cuantas líneas fácilmente podemos delegar pequeñas tareas en nuestro sistema operativo. Esto es posible porque lo que escribimos se interpreta y al “correr” el programa se compila a lenguaje de máquina, y al modificarlo de nuevo se vuelve a compilar, lo complejo no lo vemos pero está allí en los archivos .pyc o .pyo
  3. Su entorno de desarrollo (IDE) está escrito para varios sistemas operativos: Unix, GNU/Linux, macOs y otros más.
  4. Es multiparadigma, es decir, no obligan al programador a atarse a un estilo de programación, más sin embargo se deben acatar las buenas prácticas de sintaxis que son muy sencillas pues principalmente se basan en el indentado ya que existen pocas etiquetas y símbolos, en comparación con otros lenguajes.
  5. Es considerado un lenguaje “de pegamento” ya que une rápidamente complejas piezas y situaciones en páginas web, bases de datos y “sockets” de internet.
  6. Python es interactivo: podemos escribir línea por línea y podemos usarlo hasta como una simple calculadora. Dada esta facilidad nosotros comenzaremos este tutorial de esta manera.
  7. Es software libre (más no gratuito: lo que desarrollemos debe llevar la licencia de uso heredada para así, a futuro, forme parte del entorno de desarrollo: un ciclo sin fín que cada día fortalece nuestra filosofía de vida).

El “Zen” de Python.

Por “Zen” se entiende lo que es una forma de meditación (budismo) nacida en la India y perfeccionada y bautizada en Japón con el nombre de “Chan” y que luego se latinizó en Zen. Pues bien el lenguaje Python tiene una base firme en las siguientes líneas que es muy recomendable conocerlas (y analizarlas) antes de siquiera escribir nuestra primera línea de código:

  1. Hermoso es mejor que feo.
  2. Explícito es mejor que implícito.
  3. Simple es mejor que complejo.
  4. Complejo es mejor que complicado.
  5. Plano es mejor que anidado.
  6. Disperso es mejor que denso.
  7. La legibilidad cuenta.
  8. Los casos especiales no son suficientemente especiales como para romper las reglas.
  9. Aunque de hecho está lo pragmático, gana a la pureza.
  10. Los errores nunca deberían dejarse pasar silenciosamente.
  11. A menos que se silencien explícitamente.
  12. Cuando te enfrentes a la ambigüedad, rechaza la tentación de adivinar.
  13. Debería haber una — y preferiblemente sólo una — manera obvia de hacerlo.
  14. Aunque puede que no sea obvia a primera vista a menos que seas holandés. (NT: Guido van Rossum es holandés)
  15. Ahora es mejor que nunca.
  16. Aunque muchas veces nunca es mejor que *ahora mismo*.
  17. Si la implementación es difícil de explicar, es una mala idea.
  18. Si la implementación es sencilla de explicar, puede que sea una buena idea.
  19. Los espacios de nombres son una gran idea — ¡tengamos más ideas como esas!

En realidad este Zen es la norma número 20 de las Propuestas de Mejora de Python, mejor conocidas en idioma inglés como “Python Enhancement Proposals” o PEP’s. Esta que presentamos es la famosa PEP20 (todas están en este enlace web).

Podemos decir que las PEP son como artículos de una Constitución y cada una de ellas tocan temas tales como, por ejemplo, “Guía de Estilo para el lenguaje C” (Nº 7) y “Guía de Estilo para lenguaje Python” (Nº 8) -tal como dijimos, está Python fuertemente influenciado por el lenguaje C-. Cuando estemos muy avanzados en programación y tengamos alguna influencia en la comunidad desarrolladora de software tal vez tengamos que leer alguna de ellas, ya sea para definir el rumbo de un proyecto o incluso para dirimir alguna diferencia con nuestros colegas compañeros de trabajo: allí están las “leyes” para ello.

Casos exitosos con lenguaje Python.

Una lista completa la podéis hallar en este enlace, no obstante nombraremos algunos casos que nos llaman la atención:

  • “Industrial Light & Magic” empresa especializada en gráficos computarizados para estudios de cine, ha creado “efectos especiales” en películas famosas y/o taquilleras.
  • “D-Link” en Australia utiliza Python para actualizar los “firmwares” en sus enrutadores alámbricos e inalámbricos que no pueden iniciar por sí mismos, lo cual aumentó su productividad en un 800% contra el método anterior utilizado.
  • El laboratorio Astra-Zeneca usa Python para el trabajo colaborativo en el descubrimiento de nuevos principios químicos activos en los medicamentos.
  • Fuera del campo de la Python.org existe un grupo de astrofísicos denominados “Enanas Marrones en la ciudad de Nueva York” (Dwarf Brown in New York City –DBCNY-) que mantienen una sección especial para publicar sus programas hechos en este lenguaje, ¡asombroso!
  • Y son muchos casos más, pero uno que NO nombran allí es el caso de FreeCAD que lo utilizamos para diseño de piezas, arquitectura e incluso mecanismos; dicho lenguaje está implementado de manera nativa, si desean revisen nuestro artículo sobre FreeCAD publicado no hace mucho tiempo.

Por último lo hacemos notar: las nuevas generaciones gustan de aprender el lenguaje Python, para muestra un botón (gracias al sr. “Tauceti” de @Conatel por la foto).

 

“¡Hola mundo!” con lenguaje python.

Modo interactivo.

Comenzaremos por invocar el intérprete de comandos que nos permitirá trabajar de manera interactiva con Python. Comenzaremos por abrir una ventana terminal (si usted no sabe cómo, puede estudiar primero nuestro tutorial sobre “bash” o línea de comandos). Debemos hacer la salvedad de que debemos escribir simplemente el comando “python3” para que nos ejecute la última versión que tengamos instalada. De no ser así por favor revise de nuevo arriba donde describimos cómo instalar Python en nuestros ordenadores.

Una vez que introduzcamos el comando y presionar la tecla Intro o Enter veremos el entorno interactivo del que tanto hemos hablado (y dado loas). Apreciamos entonces la versión que tenemos, así como unos comandos adicionales para mayor información. Lo que no nos dice es que para volver a la línea de comandos shell: debemos escribir “quit()” y presionar la tecla Intro o Enter. De ahora en adelante damos por sentado que cuando nos referimos a introducir un comando con o sin opciones adicionales, debemos presionar la tecla Intro o Enter.

python3 quit()
python3 quit()

Ya aprendimos el comando, pero para mayor velocidad con el teclado recomendadmos utilizar la combinación de teclas CTRL+D (es decir, con la mano derecha presionamos y mantenemos presionada la tecla control del lado derecho MIENTRAS con la mano izquierda presionamos una sola vez la letra “D” -mayúscula o minúscula, no importa- y luego soltamos ambas). Una vez que estemos de nuevo en la línea de comandos shell podemos verificar de nuevo  cual versión de Python tenemos instalado, para lo ello debemos ingresar el siguiente comando, tal cual:

python3 --version

En el ambiente GNU/Linux se acostumbra “pasar” los opciones de comando con dos guiones más una palabra clave única, en este caso “–version“, ésta es la versión larga, la que consideramos nemotécnica (evidentemente en este caso en inglés coincide con nuestro idioma castellano… O CASI ¡porque le falta el acento!). Para rematar esta idea, que en realidad son dos, acotamos lo siguiente:

  • Las opciones “cortas” de los comandos de terminal (fuera del interpréte interactivo) solo llevan un guión y 1, 2 ó 3 letras como máximo haciendo hincapié en que se distingue de mayúsculas y minúsculas. Por ello, si queremos saber la versión del Python que tenemos con la opción corta debemos escribir “Python -V” ya que si utilizamos “-v” lo que veremos son todos los módulos que tenemos instalados, y la lista es larga. Probad y volved por favor 😉 .
  • La otra idea, ya que hablamos de minúsculas y mayúsculas, debemos tocar el tema de nuestro idioma, en particular la letra “ñ” y los acentos graves (y en francés e italiano los acentos agudos). Ya en una entrada anterior explicamos como lidiar con ellos en el lenguaje HTML y allá es bastante más complicado que acá en Python.

Lo que debemos hacer en nuestros archivos con nuestras líneas de programación para que soporten todos los lenguajes a nivel mundial (UNICODE) es colocar la siguiente línea al principio de cada fichero:

# -*- coding: utf-8 -*-

Recordad que os hablamos en párrafos anteriores acerca de las PEP: en este caso es la PEP263 la que especifica y acota el tema. Por supuesto, hay más detalles al respecto, y para ello podemos leer (en inglés) las recomendaciones de un ingeniero de software, el señor Evan Jones en su blog personal. Pero no os preocupéis, por ahora esto que os explico es más que suficiente para expresar correctemente nuestros mensajes a nuestros usuarios con nuestras aplicaciones escritas en Python.

“¡Hola mundo!”

Pues ya estamos casi listos para escribir nuestro primer programa en lenguaje Python. Dijimos que vamos a utilizarlo de manera interactiva para aprender los comandos uno a uno de manera práctica pero primero escribamos uno y lo guardaremos como archivo de texto pero con la extensión .py

Se acostumbra nombrar los archivos con esta extensión para nosotros saber rápidamente qué contiene e incluso algunos sistemas operativos lo utilizan para ejecutar una aplicación que es capaz de leer y mostrar dicho archivo. Luego veremos que en GNU/Linux esto no es necesariamente así, esperad por favor.

Vamos entonces a usar nuestro editor de texto favorito y escribamos estas líneas y guardemos el archivo con el nombre “que_hubo_mundo.py”:

# -*- coding: utf-8 -*-
def hola():
  print('¡Qué hubo mundo!')
hola()
  • La primera línea declara que vamos a trabajar con UTF8 para soportar caracteres de lenguajes humanos a nivel mundial.
  • La segunda línea DEFine una función, la cual no necesita argumento o parámetro seguido de “:” lo cual indica que las líneas que vienen pertenecen a la función declarada.
  • Debemos indentar la tercera línea, con un espacio al menos. Si hubieran más líneas, deberán tambien tener el mismo número de espacios. Tened cuidado con vuestro procesador de texto: no es lo mismo pulsar la tecla TABULADOR (TAB) que presionar la barra espaciadora para ingresar espacios en blanco. Nosotros recomendamos, si el editor de texto lo permite, configurarlo para que cada vez que presionemos TAB nos inserte 3 espacios en blanco (o incluso 2 si os parece).
  • Observad también que la tercera línea tiene un acento, todo este trabajo que hicimos fue no sólamente para que se mostrara correctamente el caracter: es que Python se niega a compilar si no hacemos esto.
  • En este punto os tenemos que confesar algo: esto no es necesario para Python versión 3.X. Esto lo hicimos totalmente a propósito porque consideramos que es importante para propósitos a nivel de sistema operativo y es una de las características que vale la pena conservar de la versión 2.X
  • Con la cuarta línea llamamos la función declarada, la cual tiene la instrucción de imprimir el mensaje deseado.
  • Otra línea que consideramos debe contener todos nuestros archivos en Python es la siguiente especificación:
#!/usr/bin/python3

Esto le indica al sistema operativo, por si quedare alguna duda, con cuál programa abriremos el archivo en cuestión; recordemos el punto 2 de la PEP20: “Explícito es mejor que implícito”.

Nuestro primer archivo quedaría de la siguiente manera:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
def hola():
print('¡Qué hubo mundo!')
hola()

Por último llamamos al archivo con el siguiente comando:

python3 que_hubo_mundo.py

“¡Qué hubo, mundo!” en modo interactivo.

Ya tenemos todas las intrucciones para trabajar en modo interactivo, podemos ahora ejecutar nuestro primer programa en modo interactivo, aquí veréis lo que debemos escribir, (si tenéis algún error revisad los pasos anteriores que describimos en sumo detalle):

¡Qué hubo mundo! en Python interactivo
¡Qué hubo mundo! en Python interactivo

Nota: en la imagen olvidamos escribir “#!usr/bin/python3”, fe de errata.

Trabajando con cadenas de texto en modo interactivo.

Ya vimos rápidamente como declarar una función para imprimir por pantalla un mensaje e nuestros usuarios. Tal vez vamos muy rápido. Por ello veremos cómo trabajar con texto y caracteres y pasamos a considerar varios puntos:

  • Podemos utilizar pares de comillas simples o pares de comillas dobles para encerrar nuestro texto uno que abre y otro que cierra, y si utilizamos ambos pues debemos anidarlos debidamente.
  • En el caso de nuestra habla coloquial (gracias al comediante Joselo que popularizó la contracción de la palabra “para”) no podemos escribir una sola comilla simple sin su correspondiente par.
  • Es entonces que pasamos a considerar los caracteres especiales: deben ser precedidos por una barra “\”, lo cual trae una reminiscencia con el lenguaje C.
  • Como vimos el caracter de numeral o almohadilla “#” es utilizado para indicarle ciertas configuraciones especiales a Python, siempre y cuando estén al inicio del fichero contentivo de instrucciones. Pero también es utilizado para poder colocar comentarios, todo lo que está a la derecha de “#” y hasta el final de la línea es mostrado más no compilado (excepto si están al inicio del archivo, repetimos).

Con la ayuda de la tecla numeral (como mejor la conocemos aquí en América) os publicamos unos cuantos ejemplos comentados y hechos en la cónsola interactiva de Python3:

python3 uso de cadenas de texto
Python3 uso de cadenas de texto.

Para que no se impriman por pantalla la comilla inicial ni la comilla final haremos uso de la función que usamos en nuestro programa que_hubo_mundo.py: la función nativa o integrada print(). Decimos que es nativa porque no es necesario definirla ni llamarla, ya Python “sabe” a que nos referimos con ella. Dicha función, además hace un retorno de carro, es decir, el “cursor” baja una línea y se ubica a la parte izquierda de la línea. Por ello, mediante ejemplos en modo interactivo, aprenderemos en detalle el uso de la función print().

python función print() linea nueva
Python función print() linea nueva.

¿Véis qué bonita quedó la frase de Don Vicente Ignacio Antonio Ramón de Emparan y Orbe? Fue bonita en 1810 y quedó bonita hoy también gracias a la línea nueva que insertamos dentro de la función print(“\n”), el cual es un “caracter de escape”. Debemos hacer la salvedad de que si queremos mostrarle a nuestros usuarios una ruta de archivo o carpeta debemos indicarle a print() que imprima en modo raw “r” para que se muestre correctamente la vía:

print(r'\home\jimmy\python3\nombres')

A lo anterior le decimos “nueva línea” para que se asocie en nuestra memoria con “\n”, pero en realidad se llama retorno de carro, como arriba explicamos. Bien podemos usar tantas nuevas líneas como queramos o bien podemos usar otra figura: el entrecomillado triple por pares:

python entrecomillado triple
Python entrecomillado triple.

Observemos que para prevenir un retorno de carro en cada línea podemos finalizarla con una barra “\” de esta manera incluso aunque escribamos múltiples líneas se mostrará como una sola (por supuesto si usamos varias barras “\”):

python entrecomillado triple con barra al final de cada línea
Python entrecomillado triple con barra al final de cada línea.

Consideraciones al trabajar en modo interactivo.

Para mostraros los ejemplos didácticos hemos cerrado y abierto varias veces el modo interactivo de cónsola, a fin de hacer claro y diáfano el código, valga la redundancia. Más adelante veremos el cómo limpiar la cónsola, lo cual es un poquito avanzado pero por lo pronto recopilaremos algunos detalles para ahorrarnos trabajo en el teclado.

  • Con tecla FLECHA ARRIBA del teclado podemos ver la última línea introducida.
  • Si seguimos pulsando flecha arriba podremos ver las líneas anteriores, con la flecha abajo podremos a volver a nuestra posición original hasta alcanzar una línea en blanco, osea la línea nueva de comando.
  • El cerrar y abrir no influye para que Python recuerde estas líneas, siempre las “recordará”.
  • En general se comporta como la línea de comandos o “shell” en ventana terminal de nuestro sistema operativo GNU/Linux.

Operaciones con cadenas de texto en modo interactivo.

Ya conocemos cómo mostrar nuestros resultados por cónsola, ahora vamos a realizar unas cuantas operaciones con cadenas de texto, ¡preparados y preparadas que esto se pone interesante! -o sería la taza de café que me tomé 😉 -.

Lo que vamos a practicar es el colocar una cadena de texto en una variable y haremos operaciones de concatenado con ella:

python print() uso de coma como separador
Python función print() con uso de coma como separador.

La técnica de indexado de Python nos permitirá practicar nuestra programación, tal vez el ejemplo es tonto pero es ilustrativo de las capacidades de Python, veamos:

  • Cuando almacenamos una cadena de texto en una variable, automáticamente Python le asigna una “posición” en memoria para cada una de sus letras (¿otro parecido con el lenguaje C, qué piensan ustedes?).
  • Debemos tener siempre presente que el primer caracter siempre será el número cero (0), esto es así para la mayoría de las matrices en computación (una cadena de texto es una matriz de una fila por “ene” columnas, es decir, la cantidad de caracteres que contenga)

Podemos hacer una “chuleta” mental con la palabra “murciélago” para ayudarnos a recordar, esto NO es código para Python, es para nosotros los humanos con propósitos educativos:

 +---+---+---+---+---+---+---+---+---+---+
 | m | u | r | c | i | é | l | a | g | o |
 +---+---+---+---+---+---+---+---+---+---+
   0   1   2   3   4   5   6   7   8   9
 -10  -9  -8  -7  -6  -5  -4  -3  -2  -1

Recordando lo anterior podemos volver a trabajar con nuestra frase “nagua” y seremos auxiliados por la estructura de programación “mientras que” (while condición :), pongan atención:

python indexado de una cadena de texto
Python indexado de una cadena de texto

Podemos evitar que la palabra quede “en modo vertical” agregando un argumento clave a la función print() llamado end al cual le podemos asignar una cadena none (o nula) o el caracter que deseemos y además nos evita el retorno de carro. La línea cuatro quedaría de la siguiente manera:

>>>  print(texto[k], end='')
>>>  k = k + 1
>>> print()

Pruebenlo en su cónsola interactiva y regresen, por favor. ¿Notaron para qué es la última linea agregada?

Si no incrementaramos la variable auxiliar entraríamos en un ciclo sin fin, lo que comúnmente la gente llama “quedó colgada la computadora“. De una vez vamos trabajando con números como introducción a la siguiente sección pero antes veamos un ejercicio para separar en sílabas esta esdrújula que nos ocupa:

python indexado de texto práctica para separar en sílabas
Python indexado de texto práctica para separar en sílabas

Nota: Si denotamos texto[:3] , es decir , obviamos desde dónde comienza el trozo que queremos, Python asume que desde el principio; y si denomatos texto[8:] asume que es hasta el final de la cadena de texto. Por ello la línea de práctica de la figura quedaría escrita de una manera más elegante (punto #7 de la PEP20) y así “no vemos” el elemento 10, que no existe:

>>> print(texto[:3]+texto[3:6]+texto[6:8]+texto[8:])
murciélago
>>> # Podemos referirnos con índices negativos
...
>>> print(texto[-7]+texto[-6]+texto[-5])
cié

Una cosa es el índice de un elemento y otra cosa muy distinta la función que devuelve un rango de elementos, de hecho existe una función numérica que nos devuelve un rango específico: range() y la utilizaremos en otra sección importante denominada “listas”. Por ahora podemos volver a nuestro ejemplo con la cadena de texto “nagua” para practicar sobre nuestros propios pasos:

python no soporta modificaciones a una variable indexada las listas sí
Python no soporta modificaciones a una variable indexada, las listas sí.

Como vemos la variable completa la podemos sustituir por cualquier otra cadena de texto, pero toda completa, cada uno de los elementos indexados son de “sólo lectura”. Las listas, en cambio, sí que soportan modificaciones de sus elementos pero se deben declarar de manera diferente, antes de pasar a ello terminemos primero de aprender sobre los números en Python.

Trabajando con números en modo interactivo.

Con Python podemos hacer operaciones matemáticas dignas del quinto grado de educación primaria: sumas, restas, multiplicaciones y divisiones (exactas, inexactas, cociente y resto), mirad el ejemplo práctico, pensamos no necesita mayores explicaciones que las mínimas necesarias:

python operaciones numéricas (incluyen uso de variables) en modo interactivo
Python operaciones numéricas (incluyen uso de variables) en modo interactivo

Otra operación numéricas BÁSICAS, es decir,  soportadas sin llamar ninguna librería o modulo es la potenciación:

>>> 7 ** 2 # Area de un cuadrado de 7 unidades de lado.
49
>>> 2**10  # Dos elevado a la décima potencia
1024

Trabajando con listas en modo interactivo.

Las listas se diferencian de los indexados en dos aspectos: como se declaran y que es posible no solo modificar su contenido, sino también agregar o quitar elementos. Otra ventaja es poder formar listas anidadas, pero veamos unos ejemplos prácticos por cónsola:

python listas en modo interactivo visualización con ciclo for~in: y print()
Python listas en modo interactivo visualización con ciclo for~in: y print()

En esta imagen presentamos un nuevo elemento de programación: el ciclo for. Dicho ciclo sustituye al que aprendimos primero, el ciclo while que necesita una instrucción o evento para poder salir del mismo. En cambio el ciclo for recorre la lista automáticamente, uno a uno, cada uno de los elementos (recordar que cada lista tiene implícito sus índices numéricos) esto nos ahorra el uso de una variable numérica auxiliar que usamos en el ciclo while.

Sin embargo, si queremos imprimir solamente los elementos 1 y 2 sí que debemos usar una variable numérica auxiliar con la función range() que avizoramos en la sección de elementos indexados -cadenas de texto-. Lo que debemos escribir es lo siguiente:

>>> for k in range(1,3): # Utilizamos un ciclo 'for' con 'range'
...  print(ciudades[k], end=",")
... 
Maturín,Cumaná,>>> print()
>>>

Observen el último print() y que labor cumple, pronto lo revisaremos de nuevo. Otro elemento que volvimos a utilizar es el argumento especial end=”,” para la función print() la cual omite el retorno de carro e inserta el caracter que le asignemos entre comillas por medio del símbolo de igualdad. En este caso es una coma, así la lista  impresa termine en una coma sin ningún otro elemento (esto es así para que tengamos en mente el concepto de los delimitadores: por ejemplo los archivos CSV “Comma Separated Values”)

Analizemos este código, mejorado, de la última imagen -captura de pantalla- de la cónsola interactiva de Python:

python ciclo for con else
python ciclo for con else

En seguida aprendemos que podemos agregar elementos a la lista mediante el comando .append(elemento) tantas veces como necesitemos. También el ciclo for recibe los datos de la función range: serían 3, 4, 5 y 6 pero ¡un momento! si le metemos seis como índice a la lista nos dará error, ya que va de solamente va de 0 a 5 ¿y el 6? Pues el seis si que se ejecuta por medio del comando else el cual nos permite imprimir un retorno de carro al final de la lista -de nuevo repetimos que finaliza cada línea impresa sin retorno de carro más coma por el argumento end de la función print()-.

Para no perder el hilo de aprendizaje seguimos agregando ciudades de Venezuela a la lista, pero con una variante: hemos nombrado solamente capitales de estado (y de municipio, implícitamente) pero ahora vamos a agregar otras ciudades del estado Carabobo, capitales de municipio. El chiste del asunto es que queremos esas tres ciudades (Valencia, Naguanagua y San Diego) “aparte” dentro de la lista de capitales de estado. Aprendamos a hacer eso:

python listas sublistas y elementos indexados
python listas sublistas y elementos indexados

Como pueden ver, una lista puede estar contenida otra lista (si os gusta utilizad el término sublista) y podemos nombrar con un segundo índice (juntos pero separados por paréntesis rectos o corchetes) a la ciudad deseada, pero ¿Qué pasa si un elemento con contiene una sublista? Aquí es cuando muchos programadores se frustran por la respuesta que da Python: muchos esperarían un error (no existe) sin embargo devuelve el elemento en sí de manera indexada y ya sabemos lo que esto significa, su contenido es de solo lectura, algo contradictorio proque estamos trabajando con listas. Abran su mente a Python y mantenganse siempre alerta y receptivos.

El ciclo condicional y los comandos de interrupción y continuación.

Ya está en nuestra mente el concepto de else para el ciclo for y ahora vamos a ver el ciclo condicional. Como era de esperarse, el condicional corresponde a la palabra “if” en inglés, seguido de una condición a evaluar, que de ser cierta ejecutara una serie de órdenes perfectamente indentadas y si la condición no se cumple ejecutara otra serie de órdenes, también perfectamente indentadas. Para ello haremos un muy sencillo ejemplo que ilustra lo aquí expresado:

python ciclo condicional if else
python ciclo condicional if else

OJO aquí nos volvimos repetitivos y la idea es escribir menos código, no más, si tenemos varios pares de números que comparar no vamos a estar repitiendo y repitiendo la estructura. Para ello usaremos las funciones a fondo en una próxima entrada, pero no teman, ya en nuestro programa que_hubo_mundo.py definimos una pequeña función, así que ya hemos adelantado (ver inicio de este artículo).

Por lo pronto vamos a escribir un ciclo for asumiendo que conocemos el número de pares de cifras que vamos a comparar y conoceremos una nueva función nativa: input(), que como podréis imaginar acepta los número que ingresemos por nuestro teclado y los asigna a una variable. Además, como trabajamos con números necesitaremos la función int() que convierte los caracteres ingresados a números enteros. Veamos:

python condicional if else con input() para comparar varios pares de números
python condicional if else con input() para comparar varios pares de números

A estas alturas hemos de comprobar que ya se queda corto el modo interactivo para nuestros propósitos: el más mínimo error y hay que comenzar de nuevo la estructura. Por ello a aprtir de la próxima entrada (funciones del usuario, osea, nosotros, ¡EJEM! programadores) volveremos a como empezamos: escribiendo en un editor de texto. Nosotros preferimos gedit pero usad el que más os parezca sencillo y accesible. Como tarea os dejamos un ejemplo tomado de la propia página web tutorial de Python y que sirve para hallar números primos y combina varias cosas que hemos aprendido, NOTAD los espacios indentados, trascribidlo a vuestra cónsola y probadlo, por favor:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print(n, 'equals', x, '*', n//x)
...             break
...     else:
...         # loop fell through without finding a factor
...         print(n, 'is a prime number')
...

<Eso es todo, por ahora>.

Fuentes consultadas.

Enlaces web en idioma inglés:

Enlaces web en idioma castellano:

python-easy-ftp-sync

Introducción.

El objetivo de escribir python-easy-ftp-sync persigue como fin último la creación de un programa por línea de comandos escrito en lenguaje Python versión 3.4.3 para sincronizar dos carpetas (y sus subcarpetas) por medio de FTP (File Transfer Protocol), así de sencillo, por ello lo de parte del nombre “easy” (fácil -o tranquilo- en idioma inglés). Por supuesto que no vamos a descubrir el agua tibia, antes que nosotros muchos autores han escrito sobre el tema, y lo grandioso del software libre -y la ciencia moderna en sí misma- es que no tenemos que partir desde cero (aunque sería un muy buen ejercicio) sino que podemos estudiar esos trabajos anteriores y sobre ellos construir nuestra herramienta.

Antecedentes.

Hemos incluido para su estudio los más variopintos proyectos, e incluso algunas preguntas con propuestas incluídas. También hemos descartado muchísima información que consideramos no conducen a nada relevante, así que se preguntarán ¿qué hace eso publicado en internet? Las razones son dos, al menos:

  1. “O inventamos o erramos”: evidentemente errar es lo más común, y aunque parecen inútiles esos datos (inútil no es igual que irrelevante) igual nos aporta información sobre por donde no encaminar nuestros pasos.
  2. Los buscadores o arañas web (insultando la inteligencia salvaje de las arañas) van por ahí recolectando y guardando bytes, y toman todo sin mayor discriminación, por ello podemos nosotros acceder a esos resultados fallidos. Las arañas, por otra parte, tejen su red, trepan por sus hilos y allí cae todo tipo de presa, pero ellas saben cuál víctima comer y cual bocado soltar y desechar y luego reparar su propia telaraña para alimentar sus crías y tener descendencia evolucionada; ¡menuda diferencia!

 

Fuentes consultadas.

Enlaces web en idioma castellano:

Enlaces web en idioma inglés: