Download guía python: cadenas de texto

Document related concepts
no text concepts found
Transcript
http://www.maestrosdelweb.com/guias/#guia-python
Guía Python
Python es uno de los lenguajes de programación multiparadigma, más potente y que menor curva de aprendizaje demanda. Con
Python ...
Visita Guía Python para mas información.
Capítulos
1. Programación Orientada a objetos en Python
2. Métodos y Propiedades en Python
3. Listas, tuplas, diccionarios y estructuras de control
4. Trabajando con templates, archivos y condicionales
5. Excepciones, helpers y refactoring
6. Herencia, relación entre dos o más clases
7. Testeando código con doctest en los comentarios
8. ¿Qué te gustaría aprender en el próximo capítulo?
9. Interfaces gráficas con wxPython
10. Finalizando con Python y MySQL
11. Dos héroes de Python nos cuentan sus experiencias
12. Cómo se utiliza Python y Django en Mejorando.la
13. Sigamos aprendiendo Python
14. Conociendo a fondo el repositorio de Mejorando.la creado con Python y Django
15. Guía Python: Conociendo a detalle las secuencias
16. Ventajas para los que se ponen la camiseta de python
17. Guía Python: Cadenas de texto
18. Guía Python: Expresiones Regulares
19. Guía Python: Manejando archivos, diccionarios y funciones
PROGRAMACIÓN ORIENTADA A OBJETOS EN
PYTHON
Python es uno de los lenguajes de programación multiparadigma, más potente y que menor curva de aprendizaje
demanda. Con Python puedes crear tanto robustas aplicaciones de escritorio como Web, con muy pocas líneas
de código y en muy poco tiempo. En esta guía te propongo aprender Python programando “a lo grande” ¿Te
animas al desafío?
EL DESAFÍO
Comenzaremos programando con pocas introducciones. El objetivo, es que desde el comienzo, tomes tu editor
de textos favoritos, te sientes a programar a la par de cada capítulo, ejecutes tus códigos y aprendas mientras
programas.
Al final de cada capítulo, encontrarás un “chuleta” con el resumen de lo que habrás aprendido. Y con tan solo
90′ por capítulo, en 15 horas estarás en condiciones de programar, como un verdadero Pythonista.
¿QUÉ NECESITAS?
Un editor de textos
Pues desempolva tu legendario editor de textos, ese favorito que has tenido por siempre. ¿No tienes un editor
de textos favorito? Si tienes un S.O. GNU/Linux échale un vistazo a esta lista de editores de texto para Linux.
En cambio si utilizas Windows, puedes descargar Notepad++ para Windows
Instalar Python
Si utilizas un SO GNU/Linux, seguramente ya tienes Python instalado. Para comprobarlo, abre una terminal y
simplemente escribe:
python
y pulsa enter.
Nota: Si utilizas MacOS X, el procedimiento para comprobar si tienes Python instalado, es el mismo.
¿Aparece un texto como este?
Python 2.6.5 (r265:79063, Apr 16 2010, 13:09:56)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
Entonces ¡Tienes Python instalado!
¿No ves el texto anterior?
Entonces escribe:
sudo apt-get install python
Y ¡Listo! Sigue las instrucciones en pantalla y tendrás Python instalado.
En cambio, si utilizas Windows puedes lee el tutorial completo de instalaciónInstalando Python en Windows de
Ricardo Azpeitia en Foros del Web.
Ahora sí, ¿estás listo? ¡A programar!
CAPÍTULO I: PRIMEROS PASOS CON PYTHON
Hagamos algo útil. Crearemos un módulo Python mediante el cual, nos pida algunos datos y con ellos, nos
imprima un presupuesto para enviarle nuestros clientes.
MÓDULO
Un módulo es un archivo escrito en Python, con extensión .py
El resultado final tras ejecutar el módulo que crearemos, será similar al siguiente:
Este será el resultado de nuestro primer módulo hecho en Python
1. Abre el editor de textos
2.
3.
4.
5.
6.
Copia el código que está más abajo
Guarda el archivo como presupuesto.py
Abre una terminal/consola
Navega hasta el directorio donde haz guardado el archivo
Ahora escribe python presupuesto.py y ve como funciona.
TIP
Un módulo python puede ejecutarse mediante una terminal escribiendo:
python nombre_del_archivo.py
CÓDIGO FUENTE QUE DEBES COPIAR Y PEGAR EN EL ARCHIVO
PRESUPUESTO.PY
# -*- coding: utf-8 -*class ModeloDePresupuesto:
# Datos comerciales
titulo = "PRESUPUESTO"
encabezado_nombre = "Eugenia Bahit"
encabezado_web = "www.eugeniabahit.com.ar"
encabezado_email = "[email protected]"
# Datos impositivos
alicuota_iva = 21
# Propiedades relativas al formato
divline = "="*80
# Setear los datos del cliente
def set_cliente(self):
self.empresa = raw_input('\tEmpresa: ')
self.cliente = raw_input('\tNombre del cliente: ')
# Setear los datos básicos del presupuesto
def set_datos_basicos(self):
self.fecha = raw_input('\tFecha: ')
self.servicio = raw_input('\tDescripción del servicio: ')
importe = raw_input('\tImporte bruto: $')
self.importe = float(importe)
self.vencimiento = raw_input('\tFecha de caducidad: ')
# Calcular IVA
def calcular_iva(self):
self.monto_iva = self.importe*self.alicuota_iva/100
# Calcula el monto total del presupuesto
def calcular_neto(self):
self.neto = self.importe+self.monto_iva
# Armar el presupuesto
def armar_presupuesto(self):
"""
Esta función se encarga de armar todo el presupuesto
"""
txt = '\n'+self.divline+'\n'
txt += '\t'+self.encabezado_nombre+'\n'
txt += '\tWeb Site: '+self.encabezado_web+' | '
txt += 'E-mail: '+self.encabezado_email+'\n'
txt += self.divline+'\n'
txt += '\t'+self.titulo+'\n'
txt += self.divline+'\n\n'
txt += '\tFecha: '+self.fecha+'\n'
txt += '\tEmpresa: '+self.empresa+'\n'
txt += '\tCliente: '+self.cliente+'\n'
txt += self.divline+'\n\n'
txt += '\tDetalle del servicio:\n'
txt += '\t'+self.servicio+'\n\n'
txt += '\tImporte: $%0.2f | IVA: $%0.2f\n' % (
self.importe, self.monto_iva)
txt += '-'*80
txt += '\n\tMONTO TOTAL: $%0.2f\n' % (self.neto)
txt += self.divline+'\n'
print txt
# Método constructor
def __init__(self):
print self.divline
print "\tGENERACIÓN DEL PRESUPUESTO"
print self.divline
self.set_cliente()
self.set_datos_basicos()
self.calcular_iva()
self.calcular_neto()
self.armar_presupuesto()
# Instanciar clase
presupuesto = ModeloDePresupuesto()
ENTENDIENDO EL CÓDIGO
Expliquemos el código paso a paso:
ENCONDING – DEFINIR CODIFICACIÓN DE CARACTERES
# -*- coding: utf-8 -*-
Python necesita que le indiquemos la codificación de caracteres que utilizaremos. Entonces, lo indicaremos en
la primera línea del código.
ENCODING
Debe ser la primera línea del código
Sintaxis: # -*- coding: CODIFICACION -*-
CLASES EN PYTHON
class ModeloDePresupuesto:
En la programación, un objeto es una entidad provista de métodos (funciones) y atributos. Haciendo un
paralelismo con la realidad, podemos decir que:
Una persona, realiza varias acciones (funciones) como caminar, saltar, correr, comer, dormir, etc. y tiene
diferentes atributos como el color de pelo, su estatura, su peso, el color de sus ojos, etc.
Pero, decir “persona” es muy “abstracto”, pues yo no soy “persona”, todos somos personas. Yo soy Eugenia, tu
eres José ¿o no te llamas José?, tu madre es Ana (a qué adivino nuevamente: tu madre no se llama Ana) y en
definitiva, todos somosobjetos: Eugenia, José y Ana y como objetos somos una clase de persona.
Entonces:
Una clase es un “modelo” para definir objetos que pueden realizar las mismas acciones y poseen
características similares.
En nuestro caso, crearemos decenas, miles y millones (ojalá!) de presupuestos.Cada uno de nuestros
presupuestos, será un objeto que se creará en base al modelo ModeloDePresupuesto definido en la clase.
CLASES
Una clase se define mediante class NombreDeLaClase:
COMENTANDO CÓDIGO EN PYTHON
Mira las líneas que comienzan con el signo # (como esta):
# Datos comerciales
Todas las líneas comenzadas por # son comentarios en el código fuente. Y aquellos bloques de texto, encerrados
entre tres comillas dobles como éste:
"""
Esta función se encarga de armar todo el presupuesto
"""
También son comentarios, pero que además, sirven para generar la documentación de un método.
COMENTARIOS
Para comentar y documentar código en Python utiliza:
# comentario o sino """ documentación """
DEFINICIÓN DE VARIABLES EN PYTHON
# Datos comerciales
titulo = "PRESUPUESTO"
encabezado_nombre = "Eugenia Bahit"
encabezado_web = "www.eugeniabahit.com.ar"
encabezado_email = "[email protected]"
# Datos impositivos
alicuota_iva = 21
Las variables, en nuestro ModeloDePresupuesto, son las propiedades (o atributos) característicos de nuestro
presupuesto. Para definir una propiedad (atributo o variable) se utiliza:
nombre_de_la_variable = dato
VARIABLES
Las variables en Python se definen mediante nombre_variable = dato
El nombre de una variable puede empezar (y continuar) por: Mayúsculas, minúsculas, guiones bajos y
también contener números.
Algunos ejemplos:





variable
_otra_variable_
variable_con_numero_25
VARIABLE_CON_MAYUSCULAS
Variable_Mixta
TIPOS DE DATOS
Las variables pueden contener datos de diversos tipos, por ejemplo:
Cadenas de texto
nombre_de_la_variable = "Las cadenas de texto van entre comillas"
Números enteros y números flotantes
numero_entero = 100
numero_flotante = 1298.95
Datos booleanos (verdadero o falso)
variable_con_dato_verdadero = True
variable_con_dato_falso = False
REPITIENDO CADENAS DE TEXTO
# Propiedades relativas al formato
divline ="="*80
Utilizando el asterisco * seguido de un número, estamos indicando la cantidad de veces que repetiremos la
cadena de texto colocada entre comillas:
variable_1 = "a"*3
es igual que:
variable_1 = "aaa"
Si has llegado hasta acá sin problemas, eso quiere decir que has cumplido con el primer
paso para aceptar el reto de aprender Python programando. No te pierdas el segundo
capítulo.
APRENDIENDO A PROGRAMAR EN PYTHON EN 20 MINUTOS
https://www.youtube.com/watch?v=wp4DgNbGAUI
Como complemento a esta guía, te recomendamos el vídeo de Mejorando.la junto con
Arturo Jamaica aprendemos en 20 minutos a programar algo básico en Python. Te
recomiendo que vayas al minuto 37 directamente para ir a lo importante del vídeo.
MÉTODOS Y PROPIEDADES EN PYTHON
En este segundo capítulo, seguimos con nuestro ejemplo presupuesto.pyintroduciéndonos a desarrollar, los
temas pendientes del capítulo I, pero no sin antes, unos consejos para razonar errores de código como un
programador profesional:
1. No desesperes. La magia en la programación no existe. Si el código falla, es porque algo hemos hecho mal.
2. Lee detenidamente el error que Python devuelve (presta atención a las últimas líneas donde se detalla línea en la
que falla, método o algoritmo donde se produce y descripción del error). Un ejemplo:
3. Traceback (most recent call last):
4.
File "", line 1, in
5.
File "presupuesto.py", line 158, in
6.
presupuesto = ModeloDePresupuesto()
7.
File "presupuesto.py", line 152, in __init__
8.
self.set_datos_basicos()
9.
File "presupuesto.py", line 45, in set_datos_basicos
10.
11.
12.
13.
self.seleccionar_plan()
File "presupuesto.py", line 59, in seleccionar_plan
elegir_plan = int(elegir_plan)
ValueError: invalid literal for int() with base 10: ''
>>>
14. Intenta resolverlo tu mismo ¿cómo?
1. lee la descripción del error y piensa como se produjo (es decir, cuál fue el paso previo)
2. verifica la línea de código donde se produjo el error ¿sintaxis?
3. Compara el código con el del ejemplo. ¿Lo ves igual? Entonces no pierdes nada, copiándolo textualmente
de nuevo.
4. ¿Sigue fallando? busca el error en Internet (¿Google?)
15. Si no logras resolverlo, busca ayuda: aquí, en foros o listas de correo, PERO, previamente ten en cuenta:
1. Copia el error completo, tal cual se muestra y colócalo como referencia en tu post
2. Copia tu código en algún sitio que te permita compartir y corregir código públicamente, como por
ejemplohttp://pastebin.lugmen.org.ar/
3. Coloca en tu post, un enlace hacia el código del Pastebin
Pero ¿por qué tanto lío si es más sencillo preguntar y listo?. Créeme: no lo es. Podrá ser simple preguntar sin
intentar resolverlo, pero será complejo cuando comiences a leer las posibles soluciones, pues pueden no estar
basadas en la verdadera causa (nadie está allí para conocer tu código ni como se produjo el error). Y por otro
lado, si no intentas resolverlo tu mismo, te será más difícil aprender.
Ahora sí, retomemos nuestro código!
DEFINICIÓN DE FUNCIONES EN PYTHON
def set_cliente(self):
Una función (o método) es la forma de definir una determinada acción que realiza un objeto.
DEFINIR FUNCIONES
Para definir una función, se escribe:
def nombre_de_la_funcion(parámetros):
# aquí escribimos el código que realiza la acción que definimos
Los parámetros son datos contenidos en variables (una o más), que la función necesitará para realizar la
acción. No siempre son necesarios. En nuestro caso, el parámetro self indica que la función requerirá de los
atributos contenidos en la clase (los que ya vimos recientemente).
Por ejemplo: self.empresa está llamando al atributo “empresa” de la clase.
Mientras que mediante: self.empresa = 'Maestros del Web', le estoyasignando el valor “Maestros del
Web” al atributo empresa de la clase (recuerda que un atributo es una variable).
REFERENCIA A PROPIEDADES
Cuando necesito recurrir a un atributo de la clase, dentro de una función, escriboself.nombre_del_atributo
Todo el código contenido dentro de una función (es decir, el que esté debajo de la definición de la función,
con tabulado), se llama algoritmo, y es el que indicará a la función, qué es lo que debe hacer.
INTERACTUANDO CON EL USUARIO MEDIANTE
RAW_INPUT
def set_cliente(self):
self.empresa = raw_input('\tEmpresa: ')
self.cliente = raw_input('\tNombre del cliente: ')
es una función. Pero no la definimos nosotros, sino que es una función nativa de Python.
La acción que realiza raw_input('Texto que le mostraré al usuario'), es:
raw_input()
1. Muestra al usuario el texto que se encuentra entre comillas, dentro de los parántesis (es decir, que este texto es
un parámetro requerido por la función)
2. Espera que el usuario escriba algo
3. Luego, almacena lo que el usuario escribió. En nuestro caso, será almacenado en los atributos de la clase,
empresa y cliente.
FLOAT() OTRA FUNCIÓN NATIVA (PROPIA) DE PYTHON
importe = raw_input('\tImporte bruto: $')
self.importe = float(importe)
Hemos almacenado en la variable “importe” un dato ingresado por el usuario:
importe = raw_input('\tImporte bruto: $')
Luego, asignamos esa variable (importe) como valor del atributo importe, pero esta vez, es el atributo importe
de la clase:
self.importe = float(importe)
FUNCIONES NATIVAS
al igual que raw_input() es una función nativa de Python. Su acción consiste en convertir en
número flotante (es decir, con decimales), el número que le sea pasado como parámetro.
Float()
OPERADORES MATEMÁTICOS: SUMAR, RESTAR, DIVIDIR
Y MULTIPLICAR CON PYTHON
self.monto_iva = self.importe*self.alicuota_iva/100
self.neto = self.importe+self.monto_iva
Podemos realizar sumas, restas, multiplicaciones y divisiones con Python. Y podemos almacenar los resultados
de nuestras operaciones matemáticas en variables, incluso aquellas que sean atributos de la clase.
Podemos realizar operaciones matemáticas con números: 1+1 y también con variables cuyos valores sean
números: variable_1+variable_2
OPERADORES MATEMÁTICOS
Para sumar, utilizamos +
Para restar utilizamos Para dividir flotantes utilizamos /
Para dividir enteros utilizamos //
Para multiplicar utilizamos *
Para obtener el resto de una división utilizamos %
Para exponenciar utilizamos **
EL OPERADOR MATEMÁTICO SUMA (+)
txt = '\n'+self.divline+'\n'
txt += '\t'+self.encabezado_nombre+'\n'
El operador matemático + no solo sirve para sumar números. También sirve para concatenar cadenas de
texto (es decir, unirlas). Fíjate esta línea:
txt = '\n'+self.divline+'\n'
Estoy diciendo que la variable txt es igual a:
la cadena de texto ‘\n’ (\n significa salto de línea)
más el texto contenido en el atributo divline de la clase
más nuevamente, una cadena de texto ‘\n’
Luego, mira la línea que le sigue:
txt += '\t'+self.encabezado_nombre+'\n'
Estoy diciendo aquí, que a la variable txt de antes, le agregue ahora, todo lo demás:
La cadena de texto ‘\t’ (\t significa tabular)
el texto contenido en el atributo encabezado_nombre de la clase
más finalmente, la cadena de salto de línea ‘\n’
DANDO FORMATO A CADENAS DE TEXTO
txt += '\tImporte: $%0.2f | IVA: $%0.2f\n' % (self.importe,
self.monto_iva)
¿Haz visto lo difícil de entender que parece este código? Pues ¡Piérdele el miedo! Es mucho más sencillo de lo
que parece.
Python nos da la posibilidad de utilizar comodines en nuestras cadenas de texto, para poder reemplezar datos,
que necesiten un formato especial.
El comodín es el signo del porcentaje %.
A este comodín, le segurirá un patrón, que identifique el tipo de dato (formato) que queremos darle al dato.
Por ejemplo: %0.2f, está indicando que en ese lugar (en vez del comodín y el patrón), debe ir un número
flotante con dos decimales. Es decir que el patrón 0.2fsignifica número con dos decimales.
Hasta allí, entonces, asignamos comodines y patrones. Pero luego, hay que decirle a Python qué datos debe
formatear. Para ello, luego de la cadena de texto, escribimos un comodín % segudio de un espacio, y entre
parantésis y separados por coma, las variables que contienen los datos a reemplazar.
Veámoslo con otro ejemplo:
a = 10
b = 5
resultado = "%0.2f y %0.2f son dos números flotantes" % (a, b)
Eso, sería como haber escrito esto:
resultado = "10.00 y 5.00 son dos números flotantes"
IMPRIMIENDO TEXTO EN LA PANTALLA
print txt
Cuando quieres mostrar texto en la pantalla, solo debes escribir print y lo que desees mostrar.
Pueden ser tanto los datos contenidos en una variable (como en nuestro código) o directamente un dato, como
en los siguientes ejemplos:
print 25
print "Cadena de texto"
CREACIÓN DE OBJETOS
# Instanciar clase
presupuesto = ModeloDePresupuesto()
¿Recuerdas cuando hablamos de la diferencia entre clases y objetos? Decíamos que una clase es un “modelo”
que sirve como base para crear un objeto.
Pues bien. Es hora de crear nuestro nuevo objeto: un nuevo presupuesto.
CREAR OBJETOS
Para instanciar una clase y crear un nuevo objeto se escribe:
nombre_del_objeto = NombreDeLaClase()
Con lo anterior, hemos iniciado un nuevo objeto que será creado según el modelo NombreDeLaClase.
MÉTODO CONSTRUCTOR DE LA CLASE: __INIT__
# Método constructor
def __init__(self):
es la definición de una función, al igual que todas las anteriores ¿lo notas?
Pero el nombre de la función __init__, Python lo reserva para los métodos constructores.
def __init__(self):
Un método constructor de una clase, es una función que se ejecuta automáticamente cuando crea un
objeto.
Estos métodos son sumamente útiles, cuando (como en nuestro caso), necesitamos automatizar todas las tareas.
Anteriormente, vimos como crear el objeto. Si no hubiésemos generado un método __init__, lo que vimos al
comienzo cuando ejecutamos presupuesto.py en la consola, no hubiese sido posible, ya que tendríamos que
haberle dicho manualmente a nuestro objeto, que realice cada una de las acciones, por ejemplo:
presupuesto.set_cliente() para que realice la acción de crear los datos del cliente
presupuesto.calcular_iva() para que calculara el importe de IVA
Y así con cada acción definida en la clase.
Pero, el método __init__ nos facilita la vida. Pues allí dentro, os encargamos de llamar a todas las funciones
(acciones) en el orden correcto, ahorrándonos mucho tiempo:
def __init__(self):
# en las siguientes líneas, imprimo texto en pantalla
print self.divline
print "\tGENERACIÓN DEL PRESUPUESTO"
print self.divline
self.set_cliente() # aquí seteo los datos del cliente
self.set_datos_basicos()
# aquí los del presupuesto
self.calcular_iva() # calculo el iva
self.calcular_neto() # calculo el importe total
self.armar_presupuesto()
Finalmente, llamé a la función que se encarga de convertir a texto todo el presupuesto.
LLAMAR FUNCIÓN DENTRO DE OTRA
Para llamar a una función y que ésta, se ejecute en el interior de otra, se
utilizaself.nombre_de_la_funcion()
CHULETA DE LOS CAPÍTULOS I Y II
NUEVO DESAFÍO
En el capítulo de hoy, nos extendimos en cuestiones teóricas sobre referencias del lenguaje que utilizamos en
nuestro primer ejemplo. Aprendimos muchas cosas nuevas como:





Definir funciones
Utilizar funciones nativas de Python
Operadores matemáticos
Dar formato a cadenas de texto
Entre otras…
Pero algo muy importante que aprendimos es a perderle el miedo a los errores de código y tratar de
resolverlos. Y si quieres convertirte en verdadero profesional, te invito a sumarte a un nuevo desafío. A
continuación, encontrarás un código fuente. Este código, utiliza algunas cosas que ya hemos aprendido.
Además, no te diré donde, pero tiene algún que otro error.
EL RETO DE HOY
1. Copia el siguiente código en un archivo, con el nombre que se te ocurra
2. Ejecútalo en una terminal (ya sabes como)
3. Ve si puedes encontrar el (o los) error(es) y corregirlo(s)
4. Si encuentras el(los) error(es), NO lo postees así das tiempo a los demás a hacer lo mismo. Pero, puedes contar
que los encontraste y dar pistas para ayudar a otros.
5. Si NO encuentras los errores, puedes postear preguntando a quienes sí los hayan encontrado
Si aprendes a trabajar en equipo, colaborando con tus pares (ya sea ofreciendo o pidiendo ayuda)
y evitando la competencia, estarás listo para afrontar verdaderos desafíos laborales, que harán que
tu carrera se destaque.
AQUÍ, EL CÓDIGO FUENTE
# -*- coding: utf-8 -*class Modelo:
def __init__(self):
self.divisor = 23
valor = raw_input("Elige un número entero entre 0 y 100: ");
resultado = self.devolver_resultado(valor)
print "%d/%d es %d" % (valor, self.divisor, resulatdo)
def devolver_resultado(self, numero):
"""Divide el núemro por el divisor y devuelve un entero"""
resultado = numero//self.divisor
return resultado
obj = Modelo()
LISTAS, TUPLAS, DICCIONARIOS Y
ESTRUCTURAS DE CONTROL
ACTUALIZADO:
Sobre error en la llamada a self.leer_plantilla()
Debe eliminarse el primer “self” de la llamada:
txt = self.leer_plantilla(self.txt)
html = self.leer_plantilla(self.html)
¡Muy buenas Pythoneros! estamos aquí con la tercera entrega de Aprender Python Programando.
El desafío de hoy, no es apto para personas impresionables, pues ¡Bienvenid@s a un nuevo reto para
corajud@s!
¿Qué haremos? Hoy agregaremos más funcionalidad a nuestro módulo Presupuesto.
1. Diseñaremos una plantilla HTML y otra plantilla TXT para nuestro presupuesto, así, no solo se verá más
agradable, sino que además, será más fácil modificarlo
2. Comenzaremos a colocar una numeración incremental automática, a cada nuevo presupuesto que generemos
3. Podremos elegir entre guardar el archivo en formato HTML o mostrarlo en pantalla
4. Crearemos una lista de precios ¡Basta de agregar tantos datos manualmente!
Finalmente, nuestro programa se verá así:
Produciendo presupuestos en formato HTML, como el siguiente:
¿CÓMO FUNCIONARÁ AHORA?
Ahora, solo nos pedirá:
1. Los datos de la empresa y la fecha del presupuesto (igual que antes)
2. En vez de detallar el servicio y el importe, los obtendrá de una lista de precios, por lo cual, solo nos pedirá
ingresar el código correspondiente al plan:
0: Plan corporativo
1: Plan personal
2: Plan de mantenimiento
3. Calculará automáticamente el número de presupuesto, y
4. Finalmente nos preguntará si deseamos guardar el archivo:
1. Si elegimos “S” (si), guardará un HTML y nos informará el nombre del archivo (que será el número de
presupuesto con extensión .html)
2. Si elegimos “N” (no), nos mostrará el presupuesto en pantalla como lo hacía hasta ahora
3. Si nos equivocamos, y no elegimos ni “S” (si) ni “N” (no), nos avisará que ingresamos una opción
incorrecta, y nos dará la oportunidad, de volver a elegir.
¡Comencemos!
ARCHIVOS NECESARIOS
Para hacerte de los archivos necesarios, sigue los ítemes de esta lista, paso por paso:
1. Crea una carpeta llamada capitulo3
Aquí almacenaremos todos los archivos del programa
2. Dentro de la carpeta “capitulo3″, crea una subcarpeta llamadapresupuestos
Aquí se guardarán todos los presupuestos generamos en formato HTML (los que decidamos guardar)
3. Dentro de la carpeta “capitulo3″, crea otra subcarpeta llamada templates
Aquí guardaremos la plantilla HTML y otra con formato solo texto (TXT)
4. Dentro de la carpeta templates guarda los archivos template.html ytemplate.txt
Pulsa en cada archivo para descargarlos desde Launchpad. Estas serán, nuestras dos plantillas
5. Dentro de la carpeta capitulo3, crea un archivo llamado contador.txt
Será la guía que utilizaremos para calcular incrementalmente nuestros números de presupuesto.
6. Abre el archivo contador.txt, edita el contenido (está vacío), escribe 100 y guarda los cambios
Inicializamos el contador en 100. Nuestro primer presupuesto, obtendrá el número 101 y así sucesivamente de
forma incremental.
7. Dentro de la carpeta capitulo3, guarda el módulo presupuesto.py(modificado)
Explicaremos todos los cambios nuestro módulo, a lo largo del capítulo.
Finalmente, la estructura de archivos y directorios, deberá verse así:
[-]capitulo3
|_ [+]presupuestos
|_ [-]templates
|_ template.html
|_ template.txt
|_ contador.txt
|_ presupuesto.py
LA LISTA DE PRECIOS: LISTAS, TUPLAS Y DICCIONARIOS
Te voy a pedir que abras el módulo Presupuesto (presupuesto.py) y vayas a lalínea 22:
# Planes y servicios - Capítulo 3
planes = ('corporativo', 'personal', 'mantenimiento')
# Tupla
corporativo = ['Diseño Sitio Web corporativo', 7200]
# Lista
personal = ['Diseño Sitio Web básico', 4500]
# Lista
mantenimiento = ['Mantenimiento sitio Web (mensual)', 500]
# Lista
lista_precios = {'corporativo':corporativo,
'personal':personal,
'mantenimiento':mantenimiento}
# Diccionario
Como verás, hemos agregado cuatro nuevas propiedades de clase a nuestro módulo. Pero, el tipo de datos, no es
ninguno de los que ya hemos visto! Pertenecen a tres nuevos tipos de datos: tuplas, listas y diccionarios.
Tanto las tuplas, como listas y diccionarios, son una forma de almacenar varios datos diferentes, de diversos
tipos (cadenas de texto, enteros, flotantes, booleanos…) en una misma variable.
El orden en el cual estos datos se especifican dentro de la variable, se denomina índice, teniendo el primer
dato un índice 0 (cero), el siguiente 1, y así incrementalmente.
Veamos estos tres nuevos tipos de datos en detalle:
TUPLAS
planes = ('corporativo', 'personal', 'mantenimiento')

El contenido de una tupla se escribe siempre entre paréntesis ( )

Admite cualquier tipo de dato:
mi_tupla = (‘texto’, 100, 7.25, False, True)

Para acceder a uno de esos datos, se realiza por su número de índice: texto es índice 0; 100 es índice 1; 7.25 es índice
2; False es índice 3 y,True es índice 4

Para acceder a una variable por su número de índice, éste se escribe entre corchetes: print
mi_tupla[2] imprimirá 7.25 mientras que print mi_tupla[4] imprimirá True

Los datos contenidos en una tupla no pueden modificarse.
En nuestro código, la tupla planes lo que está haciendo es almacenar el nombre de los tres tipos de planes que
ofreceremos a nuestros clientes.
LISTAS
corporativo = ['Diseño Sitio Web corporativo', 7200]
personal = ['Diseño Sitio Web básico', 4500]
mantenimiento = ['Mantenimiento sitio Web (mensual)', 500]

El contenido de una lista se escribe siempre entre corchetes [ ]

Admite cualquier tipo de dato:
mi_lista = ['texto', 100, 7.25, False, True]

Para acceder a uno de esos datos, se realiza por su número de índice al igual que con las tuplas: print
mi_lista[2] imprimirá 7.25 mientras que print mi_lista[4] imprimirá True

A diferencia de las tuplas, los datos contenidos en una lista PUEDEN modificarse, accediendo a ellos por su
número de índice:
mi_lista[0] = 'otro contenido'
En nuestro código, hemos creado una lista para cada tipo de plan, donde el índice 0 (cero) será la descripción
del servicio y el índice 1, el precio de ese servicio.
DICCIONARIOS
lista_precios = {'corporativo':corporativo,
'personal':personal,
'mantenimiento':mantenimiento}

El contenido de un diccionario se escribe siempre entre llaves { }

Admite cualquier tipo de dato

Cada dato almacenado en un diccionario, debe llevar un nombre de claveantecediendo al dato:

diccionario = {'nombre_de_clave':'texto',

'numero_entero':100,

'numero_flotante':7.25,

'falso':False,
'verdadero':True}

Para acceder a uno de esos datos, se realiza por su nombre de clave:
print diccionario['numero_entero'] imprimirá 100 y si deseo modificar 100 por 125, escribo:
diccionario['numero_entero'] = 125. Es decir que al igual que las listas, se pueden modificar los datos.
En nuestro código, hemos creado un diccionario para englobar el nombre de nuestros planes (que actuará como
nombre de clave) y el valor de cada clave, será cada una de nuestras listas.
PARA SABER
Las tuplas, listas y diccionarios, admiten también como valores, otras tuplas, listas y/o diccionarios!
tupla1 = ('rosa', 'verde', 'rojo')
tupla2 = (tupla1, 'celeste')
tupla3 = ('hortensia', 'neomarica', 'rosa', 'jazmin')
lista1 = [tupla1, tupla2, 'negro', 'amarillo']
lista2 = [lista1, 'naranja']
diccionario1 = {'colores':lista2, 'plantas':tupla3}
gran_tupla = (diccionario1, 'y algo más!')
Hasta aquí vemos como acceder uno a uno a los datos de una tupla, lista o diccionario. Pero ¿qué sucede si
queremos recorrerlos todos y no sabemos cuantos índices tiene? Para ello, utilizaremos una estructura de
control llamada bucle for.
Una estructura de control es un bloque de código que permite tomar decisiones de manera dinámica,
sobre código existente.
EL BUCLE FOR
En nuestro código, la estructura de control que estamos implementado se denomina bucle for y es la que se
encuentra representada por el siguiente bloque de código (líneas 44, 45 y 46):
for plan in self.planes:
texto_a_mostrar += '(%d)%s
' % (codigo_plan, plan)
codigo_plan = codigo_plan+1
¿Por qué bucle? Porque es una acción que no se ejecuta solo una vez, sino que lo hará hasta que
una condición deje de cumplirse.
¿Qué condición? la que restringe la ejecución de ese bucle. En nuestro caso, la condición estará delimitada por
la cantidad de planes en nuestra tupla planes: for plan in self.planes:
Lo anterior puede leerse así:
[for] por
[plan] cada plan
[in] en self.planes
[:] hacer lo que sigue
Es decir que el condicionamiento (limitación) está dado por la cantidad de planes contenidos en la propiedad
de clase, planes.
Luego, en cada iteración (es decir, cada repetición dentro del bucle) voy agregando texto a la
variable texto_a_mostrar utilizando una cadena con comodines ¿los recuerdas? Utilizo el patrón d que indica
que allí irá un dígito y el patrón s indicando que lo reemplazaré por una string, tal cual lo vimos en capítulos
anteriores:
texto_a_mostrar += '(%d)%s
' % (codigo_plan, plan)
Ese texto_a_mostrar es el que se utiliza luego en el raw_input() para preguntar qué tipo de plan ofrecer.
Pero el código de plan lo genero dinámicamente ¿Cómo? Antes del bucle for, inicializo la
variable codigo_plan en cero:
codigo_plan = 0
En el bucle for, la voy incrementando en 1, con cada iteración:
codigo_plan = codigo_plan+1
¿Qué obtengo? El índice de cada plan dentro de la tupla:
Índice 0: corporativo
Índice 1: personal
Índice 2: mantenimiento
Y ¿de dónde surge la variable plan? se declara automáticamente en el bucle for:
for plan in self.planes:
CONVIRTIENDO A ENTEROS CON INT()
Siguiendo con el método anterior, destinado a la selección de planes, pido ingresar el código correspondiente al
plan, que a la vez será el número de índice del plan, dentro de la tupla planes:
elegir_plan = raw_input(texto_a_mostrar)
raw_input() retorna el valor ingresado, como cadena de texto. Pero necesito utilizarlo como número de índice!
entonces, convierto ese valor a entero, con otra función nativa de Python: int()
elegir_plan = int(elegir_plan)
ACCEDIENDO A DATOS POR SU NÚMERO DE ÍNDICE
Cuando ingresamos el código de plan (0, 1 ó 2) estamos ingresando un número de índice. Mediante:
self.plan = self.planes[elegir_plan]
Lo que estoy haciendo, es traer el “nombre” (valor) del tipo de plan almacenado en la tupla planes.
Los datos del servicio a cotizar como “descripción” y precio, los he guardado en el diccionario lista_precios.
Recordemos que al diccionario se accede por nombre de clave. Éste, lo obtuve antes
haciendo self.planes[elegir_plan].
Entonces, accedo al diccionario para traerme la lista, que contiene descripción del servicio e importe, utilizando
como clave, el nombre del plan:
datos_servicio = self.lista_precios[self.planes[elegir_plan]]
Almaceno la lista correspondiente al plan en una ueva variable llamadadatos_Servicios.Pero esta variable, es
una lista. Entonces, para obtener el servicio, debo recurrir al índice 0:
self.servicio = datos_servicio[0]
Y para obtener el importe, al índice 1:
importe = datos_servicio[1]
Finalmente, formateo el importe con float():
self.importe = float(importe)
CHULETA #1
Con nuestra lista de precios hemos aprendido sobre:
Tuplas
Crear
mi_tupla = ('texto', 100, 25.83, False)
Acceder
print mi_tupla[1] # Imprime: 100
Modificar
No se puede!
Listas
Crear
mi_lista = ['texto', 100, 25.83, False]
Acceder
print mi_lista[2] # Imprime: 25.83
Modificar
mi_lista[0] = 'Otro valor'
Diccionarios:
Crear
dict = {'clave':'dato', 'otra_clave':155}
Acceder
print dict['otra_clave'] # Imprime: 155
Modificar
dict['clave'] = 'Texto'
Recorrer listas, tuplas y diccionarios con bucle for
flores = ['rosa', 'jazmín', 'crisantemo']
for flor in flores:
print 'Flor de ' + flor
Imprimirá:
Flor de rosa
Flor de jazmín
Flor de crisantemo
Convertir un literal a entero:
literal = '234'
int(literal)
En el siguiente capítulo veremos como logramos utilizar plantillas HTML para nuestros presupuesto y como
fue el proceso para almacenarlos. Ahora, un desafío extra...
NUEVO RETO
Mirando el método que se encuentra en la línea 72 de nuestro módulo Presupuesto:
# Armar numero de presupuesto
def armar_numero_presupuesto(self):
contador = open('contador.txt', 'r+')
# Abro contador
ultimo_num = int(contador.read())
# obtengo ultimo numero
nuevo = ultimo_num+1
# genero nuevo número
contador.seek(0)
# muevo cursor a byte 0
nuevo = str(nuevo)
# convierto numero a string
contador.write(nuevo)
# sobreescribo el número
contador.close()
# cierro contador
self.numero_presupuesto = nuevo
# seteo el nuevo número
Leyendo los comentarios a la derecha de cada línea ¿Alguien se anima a explicar por qué luego de abrir un
archivo, leerlo y antes de escribirlo se utilizacontador.seek(0) para "mover el cursor al byte 0"?
Vamos a ver esto de forma detallada en el siguiente capítulo, pero ¿quién se anima a tratar de deducirlo y
explircarlo?
TRABAJANDO CON TEMPLATES, ARCHIVOS Y
CONDICIONALES
En el capítulo anterior, estuvimos modificando nuestro programa: creamos una lista de precios y agregamos
plantillas HTML y TXT para imprimir los presupuestos.
Vimos como utilizar tuplas, listas y diccionarios e incorporamos una nuevaestructura de control mediante
el bucle for en el proceso de creación y utilización de listas de precios para nuestro programa.
En el capítulo de hoy, veremos cómo logramos utilizar plantillas para generar nuestros presupuestos y
el proceso para guardarlos.
Comenzaremos con algo sencillo y sumamente práctico: lectura y escritura de archivos.
LECTURA Y ESCRITURA DE ARCHIVOS
Al trabajar con archivos, existen cuatro acciones básicas que podemos hacer con un archivo:




Abrir un archivo
Leer un archivo
Escribir/sobrescribir un archivo
Cerrar un archivo abierto
Cuando abrimos un archivo podemos hacerlo con diversos fines:



Abrirlo para leerlo
Abrirlo para escribirlo
Abrirlo para leerlo y escribirlo, etc
Para abrir un archivo se realiza mediante la clase open() de Python, cuyo método constructor recibirá
como primer parámetro, la ruta del archivo a ser abierto y como segundo parámetro, el modo de
apertura (es decir, el objetivo para el cual lo abrimos: escritura, solo lectura, lectura y escritura, etc.).
En la línea 103 abrimos un archivo para leerlo:
filename = open(archivo, 'r')
Donde archivo es la ruta del archivo pasada como parámetro en el método leer_plantilla() y la
cadena ‘r’ representa el modo de apertura “solo lectura”.
Mientras tanto, en la línea 74, abrimos un archivo no solo para leerlo, sino también para escribirlo:
contador = open('contador.txt', 'r+')
En este caso, ‘r+’ significa “lectura y escritura”.
Sin embargo, en la línea 92, estamos abriendo un archivo, pero para crearlo. Utilizando en este caso el modo
‘w’, que creará el archivo si no existe (o lo reemplazará si existe, creando uno nuevo con el mismo nombre),
preparándolo para ser escrito:
presupuesto = open(filename, 'w')
Tanto con la variable filename como con la variable contador y presupuesto, lo que estamos haciendo
es crear un objeto archivo, para luego utilizar los métodos necesarios:
objeto.read() para leer el contenido de un archivo;
objeto.write('nuevo contenido') para escribir en el archivo;
objeto.seek(numero_de_byte) para mover el cursor hacia el
y objeto.close() para cerrar el archivo.
byte indicado en el archivo;
Cuando leemos el contenido de un archivo con el método read(), éste, retorna una “cadena de texto”. Si el
contenido del archivo que estamos leyendo, es un número (como en nuestro contador.txt), obtendremos el literal
de ese número y para poder utilizarlo como “número” (por ejemplo, para hacer operaciones matemáticas), será
necesario convertirlo a entero o flotante, como en la línea 75, donde medianteint() convertimos el literal leído a
número entero:
ultimo_num = int(contador.read())
APRENDIENDO A UTILIZAR TEMPLATES EN PYTHON
IMPORTACIÓN DE MÓDULOS




presupuesto.py es nuestro módulo.
Cualquier otro archivo con extensión .py, también es un módulo.
Python, tiene sus propios módulos.
Cualquier módulo Python (propio o provisto por Python) puede ser importado.
IMPORTAR MÓDULOS
Importar un módulo significa incluir un archivo .py dentro de otro, para utilizar sus métodos.
Para importar todo un módulo, se escribe:
import modulo
Para importar solo una clase de un módulo, se escribe:
from modulo import Clase
Para importar varias clases de un módulo, se separan los nombres de las clases con comas:
form modulo import ClaseUno, ClaseDos, ClaseVeinte
En nuestro ejemplo:
form string import Template
Estamos importando la clase Template del módulo string de Python.
Esta clase, es la que nos va a permitir utilizar una plantilla para nuestro presupuesto y luego hacer
un render (reemplazo dinámico) de datos.
© XKCD This work is licensed under a Creative Commons Attribution-NonCommercial 2.5 license.
LA CLASE TEMPLATE DEL MÓDULO STRING DE PYTHON
Mira la líneaa 131 y 132 del módulo presupuesto.py:
txt = Template(txt).safe_substitute(diccionario)
html = Template(html).safe_substitute(diccionario)

Template() es una clase del módulo string de Python.

El constructor de esta clase (es decir, el método __init__) recibe como parámetro una
plantilla: Template(txt) y Template(html)

Esta plantilla, debe tener ciertos “comodines” que indicarán los datos que deben ser reemplazados.

Estos “comodines” se denominan identificadores.

Los identificadores, se escriben con un signo $ delante:$nombre_del_identificador. Estos identificadores los
hemos colocado en nuestro archivo template.txt y también en template.html

Para escribir un signo $ sin hacer referencia a un identificador, debe escaparse con otro signo $. Esto: $$ imprime $,
mientras que$nombre_identificador será reemplazado por un identificador cuyo nombre
sea nombre_identificador
Los métodos substitute() y safe_substitute() de la clase Template

Como nuestra clase Presupuesto, Template, tiene sus propios métodos:substitute() y safe_substitute()

Ambos métodos, reciben como parámetros, un diccionario de datos.

Este diccionario se compone de clave=valor donde clave, será el nombre de un identificador y valor, el dato por el
cuál será reemplazada la clave.
UN EJEMPLO SIMPLE
from string import Template
print Template('Reemplazar $identificador').substitute(identificador='valor de reemplazo')
La salida de lo anterior, será:
Reemplazar valor de reemplazo
MÉTODOS DE LA CLASE TEMPLATE
La diferencia entre el método substitute() y safe_substitute(), es que si nos olvidamos de colocar el valor para
un identificador, el primer método generará un error, mientras que el segundo, simplemente lo dejará sin
reemplazar.
Es decir que si escribimos
Template('Este es el $id1 y este $id2).safe_substitute(id1='Identificador Uno')
La salida será:
Este es el Identificador Uno y este $id2
Mientras que substitute provocaría un error.
DICT(): CREACIÓN DE DICCIONARIO DE DATOS
Ve a al línea 116 del módulo presupuesto:
diccionario = dict(nombre=self.encabezado_nombre,
web=self.encabezado_web,
email=self.encabezado_email,
titulo=self.titulo,
numero=self.numero_presupuesto,
fecha=self.fecha,
empresa=self.empresa,
cliente=self.cliente,
plan=self.plan,
servicio=self.servicio,
precio=self.importe,
iva=self.monto_iva,
total=self.neto,
limite=self.vencimiento)
Aquí lo que hicimos, fue armar un diccionario de datos con la clase dict() de Python.
Podríamos haber colocado estos pares de clave=valor dentro del métodosafe_substitute() directamente.
Pero para hacerlo más legible y prolijo, creamos este diccionario de datos, que luego lo pasamos como
parámetro a safe_substitute():
txt = Template(txt).safe_substitute(diccionario)
html = Template(html).safe_substitute(diccionario)
CHULETA PARA UTILIZAR TEMPLATES
Para utilizar una plantilla, debemos:
1. Crear la plantilla en un archivo, colocando identificadores antecedidos por el signo $ en los lugares donde
necesitemos reemplazar los datos dinámicamente.
2. Importar la clase Template del módulo string de Python:
from string import Template
3. Guardar el contenido de la plantilla en una variable:
4. # Crear un objeto archivo: Abrir el archivo en modo de solo lectura
5. archivo = open('archivo', 'r')
6. # Leer el archivo
7. contenido = archivo.read()
8. # No olvidarse de cerrar el archivo
9. archivo.close()
10. Crear un diccionario de datos con dict():
diccionario = dict(id='un valor', otro_id='Otro valor')
11. Hacer un render de datos con la clase Template y el método substitute()o safe_substitute() (este
último, es mejor para prevenir errores):
Template(plantilla).safe_substitute(diccionario)
Dónde plantilla debe ser el contenido del archivo leído previamente ydiccionario el creado mediante la clase
dict()
CHULETA DE OPERACIONES BÁSICAS CON ARCHIVOS
ESTRUCTURAS DE CONTROL EN PYTHO: IF… ELIF… Y
ELSE
¿Guardamos el presupuesto, lo imprimimos en pantalla, o…? En el capítulo anterior estuvimos hablando
sobre estructuras de control con el bucle for. Hoy, incorporamos una nueva estructura de control: el
condicional if:
if respuesta.lower() == 'n':
print txt
# si en cambio el usuario indica "n"
elif respuesta.lower() == 's':
filename = 'presupuestos/'+self.numero_presupuesto+'.html'
presupuesto = open(filename, 'w')
# Creo el archivo
presupuesto.write(html)
# escribo el contenido
presupuesto.close()
# cierro el archivo
print '\n\tEl archivo se ha guardado en '+filename+'\n\n'
# sino
else:
print '\n\tOpción incorrecta. No se guardó el
presupuesto.\n\n'
self.guardar_presupuesto(txt, html)
(ver líneas 87 a 99)
El condicional if, elif y else, es uno de los que más utilizarás en tu vida como programador. Básicamente, al
igual que otras estructuras de control, permite tomar decisiones si una determinada condición se cumple.
El razonamiento de condicionales puede representarse como sigue:
si condicion X se cumple:
hacer esto
sino, si condicion Y se cumple:
hacer esto otro
sino (si no se cumple ni X ni Y):
hacer tal otra cosa
No necesariamente el condicional debe cumplir esa estructura. A veces solo es necesario evaluar una única
condición y tomar una decisión SOLO sobre la base de si esta condición se cumple:
si condición X se cumple:
hacer esto
# fin
Ejemplo:
a = 10
if a == 10:
print 'a es igual a 10'
O también, tomar una decisión si la condición se cumple y otra si no se cumple:
a = 10
if a == 10:
print 'a es igual a 10'
else:
print 'a no es igual a 10'
Con elif se pueden tomar tantas decisiones como condiciones quieran evaluarse::
a = 10
if a == 10:
print 'a es igual a 10'
elif a == 9:
print 'a es igual a 9'
elif a == 75:
print 'a no es ni 9 ni 10, es 75'
Y si a lo anterior le agregamos else estaríamos cubriendo todas las posibilidades:
a = 10
if a == 10:
print 'a es igual a 10'
elif a == 9:
print 'a es igual a 9'
elif a == 75:
print 'a no es ni 9 ni 10, es 75'
else:
print 'a no es ni 9, ni 10 ni 75. Es un valor que no evalué'
Retomemos nuestro código. En el método guardar_presupuesto() (línea 84), lo primero que hago es preguntar si
se desea guardar o no el presupuesto, siendo las respuestas esperadas “s” (sí, lo deseo guardar) o “n” (no, no
deseo guardarlo):
respuesta = raw_input('\n\t¿Desea guardar el presupuesto? (s/n): ')
Solo espero una de esas dos respuestas. Es necesario evaluar la respuesta ingresada.
if respuesta.lower() == 'n':
El método lower() me convierte el valor ingresado por el usuario a minúsculas, para que me sea más sencillo
evaluar la condición. Básicamente la condición que estoy evaluando es: “si la respuesta fue no”.
Al evaluar una condición, la estructura de control responderá con True (verdadero) o False (falso). Si la
respuesta ingresada fue “n”, entonces la condición cumple.
Sino, si la respuesta fue “s” elif respuesta.lower() == 's': se está cumpliendo esta condición (se eligió
si).
Pero también puede suceder, que ni “s” ni “n” sean ingresados. Ésto, lo evalúo genericamente con else:
Razonando la estructura completa:
Si la respuesta es “n”, imprimo el presupuesto en pantalla
if respuesta.lower() == 'n':
print txt
En cambio, si la respuesta es “s”, creo un nuevo archivo y lo guardo.
elif respuesta.lower() == 's':
filename = 'presupuestos/'+self.numero_presupuesto+'.html'
presupuesto = open(filename, 'w')
presupuesto.write(html)
presupuesto.close()
print '\n\tEl archivo se ha guardado en '+filename+'\n\n'
Pero si la respuesta no es ni “n” ni “s”, vuelvo a ejecutar este mismo método desde el comienzo, es decir que el
método guardar_presupuesto() se llama a sí mismo:
print '\n\tOpción incorrecta. No se guardó el
presupuesto.\n\n'
self.guardar_presupuesto(txt, html)
RECURSIVIDAD
Cuando un método se llama a sí mismo, se denomina recursividad. Ésto, genera una iteración (un bucle) del
método en sí mismo. Por lo tanto, se debe ser muy cuidadoso al emplear una llamada recursiva y hacerlo solo
cuando sea estrictamente necesario y no se corra el riesgo de caer en un bucle infinito.
OPERADORES LÓGICOS Y RELACIONALES
Para evaluar condiciones no solo podemos recurrir a si “X es igual a Y”. Existen otros operadores que nos
permitirán evaluar diferentes condiciones. Estos operadores se denominan operadores lógicos que nos
permitirán evaluar múltiples condiciones en un mismo proceso y operadores relacionales, que nos permitirán
evaluar la relación existente en una condición dada.
OPERADORES RELACIONALES
==
Igual que
!=
Distinto que
<
Menor que
>
Mayor que
>=
Mayor o igual que
<=
Menor o igual que
Usos:
if
if
if
if
if
if
a
a
a
a
a
a
== b # si a es igual que b
!= b # si a es distinto que b
> b # si a es mayor que b
>= b # si a es mayor o igual que b
< b # si a es menor que b
<= b # si a es menor o igual que b
OPERADORES LÓGICOS
and
(y)
or
(o)
not
(no)
Los operadores lógicos permitirán evaluar múltiples condiciones en una misma instancia:
si condicion_1 and condicion_2 or condicion_3
and evalúa si todas las condiciones se cumplen, es decir, que todas las condiciones deben retornar True
(verdadero)
a = 10
b = 5
if (a == 10) and (b != 10):
print 'Hola Mundo'
Se lee: Si (a es igual que 10) y (b es distinto que 10)
Como ambas condiciones se cumplen se imprimirá Hola Mundo.
Pero en el siguiente código:
a = 10
b = 5
if (a != 10) and (b == 5):
print 'Hola Mundo'
La primera condición NO se cumple, entonces no se imprimirá nada.
or evalúa si alguna de las condiciones se cumple. Con que una sola condición se cumpla, se ejecutará la acción.
a = 10
b = 5
if (a != 10) or (b == 5):
print 'Hola Mundo'
Se lee: si (a es distinto que 10) o (b es igual a 5).
La primera condición, no se cumple. Pero la segunda sí se cumple. Entonces, imprimirá Hola Mundo.
not evalúa si la condición NO se cumple:
a = 10
b = 5
if not a == 10:
print 'Hola Mundo'
Se lee: si NO ES a igual que 10 (similar a escribir if a != 10)
Como la condición NO se cumple, no se imprimirá nada.
MÉTODOS DEL OBJETO STRING
Vimos anteriormente que para evaluar la respuesta dada al preguntar si se desea guardar o no el presupuesto en
un archivo, utilizamos el método lower(). Este método pertenece a la clase string de Python, y su función es la
de convertir una cadena de texto en minúsculas.
La clase string de Python, además del método lower() tiene muchos otros métodos que frecuentemente podrás
utilizar. Para hacerlo, simplemente debes utilizar la sintaxis: tu_cadena.metodo()
Otros métodos de uso frecuente del objeto string son:
capitalize()
Convierte el primer carácter de una cadena en mayúscula
upper()
Convierte toda la cadena a mayúsculas
lower()
Convierte toda la cadena a minúsculas
swapcase()
Convierte minúsculas a mayúsculas y mayúsculas a minúsculas
Más métodos del objeto string, puedes verlos en este enlace.
NUEVO RETO: CONDICIONALES HASTA EN LA SOPA!
Dado el siguiente código:
# edades
Ana = 43
Juan = 43
Pedro = 8
Roberto = 12
Melisa = 15
Helena = 3
if (Ana == Juan) and ((Helena < Melisa) or (Melisa < Roberto)) and not (Pedro > Roberto):
print 'Veo condicionales hasta en la sopa!'
else:
print 'Soñaré con condicionales'
El resultado, imprimirá la frase “Veo condicionales hasta en la sopa!”.
EL DESAFÍO
Modificando únicamente un operador lógico, debes lograr que se imprima la frase “Soñaré con
condicionales”. No se puede quitar ningún operador. Solo puede cambiarse un operador lógico por otro.
EXCEPCIONES, HELPERS Y REFACTORING
¡Bienvenid@s a la quinta entrega de Aprender Python Programando!
En el capítulo de hoy, nos vamos a poner más técnicos como verdaderos programadores. Mejoraremos no
solo la funcionalidad externa de nuestro programa, sino que además, emplearemos técnicas
de refactoring(refactorización) que nos ayuden a mejorar el rendimiento interno del programa.
¿QUÉ HAREMOS HOY?
1. Agregaremos nuevas funcionalidades al programa y mejoraremos otras existentes:
o
Haremos que tanto la fecha actual como la de caducidad de nuestro presupuesto, se calculen de forma
automática.
o Al finalizar la carga de un presupuesto, tendremos la opción de abrirlo directamente en el navegador para
visualizarlo.
o Mejoraremos el funcionamiento de la elección del plan a presupuestar, validando que el dato ingresado sea
correcto. De no serlo, se nos volverá a pedir que ingresemos una opción.
2. Haremos un refactoring del código, a fin de:
o Hacer nuestro código más legible.
o Lograr que el programa tenga un mejor rendimiento.
o Facilitar el mantenimiento y evolución del sistema.
INGREDIENTES
Nuestra “receta” del capítulo hoy, necesitará nuevos archivos e incorporará modificaciones. Lo primero que
haremos es:
Descargar archivos modificados:
presupuesto.py
Descargar los archivos nuevos:
Si lo deseas, puedes modificar el nombre de la carpeta “capitulo3″ por el nombre que desees otorgarle al
programa, o dejarlo sin cambios y guardar los siguientes archivos, dentro de ella.
constantes.py
helpers.py
HELPERS: UNA FORMA DE AGRUPAR FUNCIONES
GENÉRICAS
Hemos creado un nuevo archivo: helpers.py. En este archivo, hemos agrupadofunciones de uso común, que si
bien hoy, son utilizadas por nuestro módulo Presupuesto, al ser funciones genéricas (no son “acciones” propias
del objeto presupuesto, sino que aplicarían a cualquier otro objeto), el día de mañana, podríamos utilizarlas en
otros módulos de nuestro sistema.
HELPER
En la programación, un helper es una forma de agrupar funciones de uso común, destinadas a servir de ayuda a
otros procesos.
Un Helper se compone de funciones genéricas que se encargan de realizaracciones complementarias,
aplicables a cualquier elemento de un sistema.
Es importante mencionar, que un Helper, pueden ser, tanto funciones “sueltas” como la abstracción de un
“objeto helper” (es decir, una clase).
class Helper:
def helper_1(self):
# algoritmo
def helper_2(self):
# algoritmo
Es tan (o más) válido que:
def helper_1(self):
# algoritmo
def helper_2(self):
# algoritmo
Por lo tanto, nuestro archivo helpers.py, es un módulo que contiene “ayudantes”, comunes a cualquier otro
módulo de nuestro sistema.
DOCSTRINGS Y LA FUNCIÓN HELP() EN PYTHON
¿Quieres saber de qué trata el archivo helpers.py?
1.
2.
3.
4.
Abre una terminal
Navega hasta el directorio donde tienes almacenados los módulos del sistema
Ejecuta el intérprete interactivo de python (escribe: python)
Una vez en el intérprete, escribe:
5. import helpers
help(helpers)
El "Easter Egg" de la Guía Python
¿Haz visto el resultado? ¡Sorprendente! ¿no es cierto?
Los docstrings, no son algo nuevo. Ya los habíamos visto al comienzo de la guía. Los Docstrings no son más
que el comentario de nuestro código. Y si haz probado hacer un help(helpers), habrás podido notar
la importancia de comentar nuestro código.
La función help(), al igual int(), raw_input() y tantas otras que hemos visto, es una función nativa de Python, es
decir una función interna o función built-in. Si lo deseas, es muy recomendable ver la lista completa de las
funciones built-in de Python (en inglés).
help() ha sido diseñada para ejecutarse en modo interactivo. Cuando le es pasado como parámetro, el nombre de
un módulo, objeto, clase, método, función, etc., help() devolverá la documentación que hayamos especificado,
así como toda información adicional sobre el elemento pasado como parámetro.
CONSIDERACIONES PARA ESCRIBIR DOCSTRINGS
Cuando documentes un método o función en Python, deberás escribir los docstrings teniendo en cuenta lo
siguiente:
La documentación se escribe entre triple comillas dobles en la misma línea, justo después de la
definición def
Correcto:
def mi_funcion():
"""Aquí la documentación"""
Incorrecto:
def mi_funcion():
""" Aquí la documentación """
def mi_funcion():
"""
Aquí la documentación
"""
Definitivamente un homicidio que amerita prisión perpetua:
def mi_funcion():
"""
Aquí la documentación
"""
Una buena documentación, debe incluir la acción que realiza, lo que retorna y los parámetros que acepta.
En estos casos, se utilizan docstrings multilínea, siguiendo el próximo esquema:
"""Hace X retorna Y
Argumentos:
arg1 -- descripción de arg1
arg2 -- descripción de arg2
"""
Las especificaciones para los docstrings se encuentran oficialmente documentadas por Python en las PEP
257.
TRABAJANDO CON FECHAS EN PYTHON
Una de las modificaciones que incorporamos hoy, consiste en gestionar las fechas de confección y caducidad
del presupuesto, en forma automática. Para ello, incorporamos dos helpers:
def get_fecha_actual():
hoy = datetime.datetime.now()
fecha_actual = hoy.strftime("%d/%m/%Y")
return fecha_actual
y
def sumar_dias(dias=0):
fecha = datetime.datetime.now() + datetime.timedelta(days=dias)
nueva_fecha = fecha.strftime("%d/%m/%Y")
return nueva_fecha
Las fechas, podemos generarlas automáticamente, gracias al módulo datetime de Python:
import datetime
El método now() de datetime, retorna la fecha y hora actual:
hoy = datetime.datetime.now()
La función strftime() convierte la fecha a cadena de texto, con el formato pasado como parámetro:
hoy.strftime("%d/%m/%Y")
Con la función timedelta() podemos calcular fechas, ya sea restando, como sumando, dividiendo o
multiplicando N cantidad de días:
fecha = datetime.datetime.now() + datetime.timedelta(days=dias)
EL MÓDULO DATETIME
Este módulo, nativo de Python, nos permite realizar diferentes manejos con fechas.
Para utilizar el módulo debe importarse:
import datetime
Obtener la fecha y hora actual:
datetime.datetime.now()
Sumar, restar, multiplicar o dividir N días a una fecha:
fecha operador_aritmético datetime.datetime.timedelta(days=N)
Donde N debe ser un número entero (representa la cantidad de días a sumar, restar, multiplicar o dividir). Sumar
(+), Restar (-), dividir (//), multiplicar (*).
Dar formato a una fecha:
fecha.strftime(string_formato)
Donde string_formato será la cadena de texto que defina el formato deseado, con las directrices indicadas.
Para dar diversos formatos de fechas, las directrices disponibles, son las siguientes:
Chuleta para formato de fechas
REFACTORING
Hemos realizado algunos cambios a nuestro código, pero que sin embargo, no se reflejan al momento de utilizar
el programa.
REFACTORING
Refactoring (o refactorización / refactorizar) es una técnica de programación, que consiste en efectuar cambios
al código fuente, sin alterar el funcionamiento externo del programa.
¿Qué cambios hemos hecho?
Eliminar de la clase todo aquello que no esté relacionado de forma directa con la lógica propia del objeto. Para
ello, recurrimos a:
Refactoring #1: Eliminar la directiva print del módulo presupuesto, moviéndola a un helper.
Refactoring #2: Todos los textos a mostrar en los raw_input(), así como otros mensajes del sistema, fueron
movidos a variables de acceso global, definidas en el archivo constantes.py. Es muy importante hacer la
salvedad, de que Python, no posee el concepto de constantes como otros lenguajes.
Hablar de constantes en Python, es una cuestión lingüística pero no técnica. A los fines del lenguaje, decir
“constante” en Python, simplemente hace referencia a variables cuyo valor, se encuentra predefinido sin
necesidad de requerir modificar dinámicamente dichos datos.
Limpiamos el módulo, moviendo un método genérico que podría utilizarse en cualquier otro módulo (no
necesariamente Presupuesto). Para ello, recurrimos a:
Refactoring #3: mover el método leer_plantilla() del módulo Presupuesto, a un helper llamado leer_archivo()
logrando que éste, pueda ser reutilizado desde cualquier otro módulo.
TRATAMIENTO DE EXCEPCIONES: VALIDANDO LOS
DATOS INGRESADOS
Cuando se nos pedía ingresar el código de plan, se nos daban tres opciones: 0, 1 y 2. Pero ¿qué sucedía si por
error, en vez de 0, 1 ó 2 ingresábamos otro dato?
Si ingresábamos un dato no numérico, nos generaba un error, al intentar convertirlo de literal a entero con int().
Pero, también podía suceder que se ingresara un número mayor que 2. En ese caso, la conversión a entero no
fallaría, pero al momento de intentar acceder al plan elegido mediante self.planes[plan_elegido] nos daría un
error, ya que el índice, estaría fuera del rango de la lista.
A fin de evitar estos errores, incorporamos un algoritmo de validación, mediante eltratamiento de
excepciones.
EXCEPCIONES
Una excepción es un error inesperado que se produce durante la ejecución de un programa.
Las excepciones en Python, cuentan con dos instancias obligatorias: try y except, donde el código contenido
en try intentará ejecutarse, y se falla, el error será capturado por except, lanzando otra acción.
try:
# intentar esto
except:
# Si lo anterior falla, capturar el error
# y hacer esto otro
Opcionalmente, en Python, pueden agregarse otras dos instancias, pero opcionales: else y finally. Mientras
que else, se encarga de ejecutar el código indicado solo si no ha fallado nada, finally, se llamará siempre (falle o
no), siendo su finalidad, la de ejecutar “acciones de limpieza”.
def probar_excepciones():
dato = raw_input("Ingresar numero para pasar, letra para fallar: ")
try:
int(dato)
except:
print "ejecutando execpt, try ha fallado"
else:
print "ejecutando else, try se ha logrado"
finally:
print "finally se ejecuta siempre"
Hecha esta introducción, vayamos a nuestro módulo Presupuesto (líneas 53 a 63):
try:
elegir_plan = int(elegir_plan)
self.plan = self.planes[elegir_plan]
Intenta convertir a entero el plan elegido en el raw_input() y después, intentará obtener self.planes[elegir_plan]
(elegir_plan actúa como número de índice). Pero esta última, solo se llevará a cabo, si la primera, no genera una
excepción.
except (ValueError, IndexError):
mostrar_texto(DATO_INCORRECTO)
self.seleccionar_plan()
Si se genera una excepción, se muestra un mensaje de error y se realiza una llamada recursiva.
Pero ¿Qué hay allí entre paréntesis?
ValueError e IndexError son dos tipos de excepciones. ValueError se produce cuando el valor tratado, no
corresponde al tipo de dato esperado (por ejemplo, se ingresa un caracter no numérico). E IndexError es el
error lanzado cuando se intenta acceder, por ejemplo, a un ítem inexistente de una lista o tupla, es decir, cuando
como número de índice se pasa un valor fuera del rango (es decir, mayor a la cantidad de ítems en la tupla o
lista).
Si se desea capturar cualquier excepción, se puede utilizar:
except:
Si se desea capturar únicamente un tipo de excepción, se puede utilizar:
except TipoDeExcepcion:
Si se desean capturar varias excepciones en un mismo paso, se utiliza:
except (TipoExcepcion1, TipoExcepcion2, TipoExcepcion5):
Es posible también, capturar distintas excepciones en varios pa<strongsos:
except TipoExcepcion1:
#...
except (TipoExcepcion2, TipoExcepcion3):
#...
except TipoExcepcion4:
#...
Una alternativa muy útil, es recoger la descripción del error para poder mostrarla:
except TipoExcepcion, error_capturado:
print error_capturado
except (TipoExcepcion1, TipoExcepcion2), error_capturado:
print error_capturado
Finalmente, ejecutaremos el resto del script, si ninguna excepción ha sido lanzada:
else:
datos_servicio = self.lista_precios[self.planes[elegir_plan]]
self.servicio = datos_servicio[0]
importe = datos_servicio[1]
self.importe = float(importe)
Chuleta de tipos de excepciones
WEBBROWSER: ABRIENDO EL NAVEGADOR DESDE
PYTHON
# Mostrar presupuesto en navegador
def mostrar_presupuesto(self, archivo):
respuesta = raw_input(MOSTRAR_PRESUPUESTO)
if respuesta.lower() == 's':
webbrowser.open(BASE_DIR + "/" + archivo)
(líneas 103 a 107)
Con el módulo webbrowser nativo de Python, es posible abrir el navegador y cargar en él, cualquier archivo
HTML o dirección Web. Solo basta importar el módulo webbrowser import webbrowser y cargar la URL
mediantewebbrowser.open(URL)
UN NUEVO DESAFÍO
En este capítulo, hemos aprendido dos conceptos fundamentales, indispensables en la vida de
cualquier programador experto: la técnica de refactoring y elmanejo de excepciones. Y ninguna de las dos,
es poca cosa, ni mucho menos, pueden etiquetarse como “básicas”. Los programadores que recién se inician (e
incluso, puedo asegurarles que muchos programadores con años de experiencia), no suelen refactorizar el
código fuente de sus programas, y son muy pocas las veces que manejan excepciones dentro del código.
La tarea que nos toca hoy, es animarnos a programar como verdaderos profesionales. ¿Cuál es el desafío
entonces?
Hacer un refactoring de TODOS los archivos de nuestro sistema
¿Cómo? Muy simple. Python, tiene una serie de recomendaciones para la escritura de código, llamada “Style
Guide for Python Code”, definida en la PEP 8 – de la cual hemos hablado sutilmente en capítulos anteriores . Los verdaderos programadores Python, debemos respetar esta guía de estilos al pie de la letra, ya que
hacerlo, nos garantiza que nuestro código pueda ser fácilmente mantenible, legible, evolutivo y por sobre
todo, es una forma de respetarnos entre colegas. Escribir código desprolijo o con “estilo propio”, dificulta la
lectura. Ergo, hace que entender el código escrito por otro programador, nos lleve más trabajo.
Entonces:
1. Lee las PEP 8 (en inglés) o las PEP 7 (disponibles en español)
2. Revisa cuidadosamente los archivos constantes.py, helpers.py y sobre todo, presupuesto.py (tiene violaciones a
las PEP 8 de forma ex-profesa)
3. Haz todas las modificaciones que sean necesarias, para estandarizar el código fuente según las PEP
Si no entiendes algo de las PEP ¡pregúntalo! No te quedes con la duda. Si te aburre, o no te apasiona hacerlo
o lo consideras sin sentido, ten en cuenta que hacerlo será fundamental para tu crecimiento profesional. Te lo
garantizo!.
Es muy importante que aproveches este reto para trabajar en equipo. Puedes utilizar los comentarios para
compartir con otros programadores, las líneas de código que hayas refactorizado, así como también,
intercambiar opiniones sobre cómo sería mejor refactorizar tal o cual otra línea. ¡Aprovecha esto para
entrenarte! La capacidad para trabajar en equipo, no solo la da la buena predisposición, sino también la
práctica y el intercambio con otros programadores. Y ten en cuenta, que a la hora de buscar trabajo, sumará
puntos a tus fortalezas.
HERENCIA, RELACIÓN ENTRE DOS O MÁS
CLASES
Y ya hemos llegado al Capítulo VI de la Guía Python!
En el capítulo de hoy, agregaremos nueva funcionalidad a nuestro programa generador de presupuestos,
introduciendo con ello, un nuevo concepto de la programación orientada a objetos: herencia.
HERENCIA
En Programación Orientada a Objetos, la herencia es la relación existente entre dos o más clases. La
herencia marca una relación de jerarquía entre objetos, en la cual, una clase principal (madre) puede ser
heredada por otras
secundarias (hijas), las cuales adquieren “por herencia” los métodos y atributos de la primera (clase principal).
El objetivo de hoy, es utilizar esta característica de la POO (la herencia), para lograr dos tipos de presupuesto:
1. El presupuesto tradicional que venimos generando.
2. Un presupuesto extendido, que emita los recibos de pagos correspondientes, calculando el monto de adelanto y su
resto.
ARCHIVOS NECESARIOS
Utilizaremos todos los archivos disponibles en la carpeta “capitulo6“. En esta carpeta, encontraremos las
siguientes novedades:
UN NUEVO SUB-DIRECTORIO LLAMADO RECIBOS
En caso que decidamos ejecutar el generador de presupuestos en modo avanzado, éste, automáticamente creará
y guardará los nuevos recibos en esta carpeta.
NUEVO TEMPLATE RECIBO.TXT
Este archivo es un nuevo template, el cual emplearemos como plantilla para generar los recibos.
==========================================================================
======
$titulo Nº $numero/1
==========================================================================
======
En $ciudad a los ___ días del mes de ___________ de 20__
RECIBÍ de $cliente
La cantidad de $moneda $pago_1
En concepto de pago adelanto presupuesto Nº $numero.-
________________________
$nombre
$web
==========================================================================
======
-------------------------- >>>> cortar aquí <<<< -----------------------------
==========================================================================
======
$titulo Nº $numero/2
==========================================================================
======
En $ciudad a los ___ días del mes de ___________ de 20__
RECIBÍ de $cliente
La cantidad de $moneda $pago_2
En concepto de pago finalización de obra según presupuesto Nº
$numero.-
________________________
$nombre
$web
==========================================================================
======
UN NUEVO MÓDULO: RECIBO.PY
Este será el archivo en el que más nos concentraremos hoy. Un modelo que hereda de Presupuesto,
generando un presupuesto extendido que incluye formulario de recibo de pago.
# -*- coding: utf-8 *-*
from string import Template
from presupuesto import Presupuesto
from constantes import TEMPLATE_RECIBO, CIUDAD, MONEDA_DENOMINACION
from helpers import leer_archivo
class PresupuestoConRecibo(Presupuesto):
def __init__(self):
Presupuesto.__init__(self)
self.adelanto = 40
self.titulo = "RECIBO"
self.generar_recibo()
def calcular_pagos(self):
"""Calcula el monto correspondiente al adelanto y resto del
trabajo"""
total = self.neto
self.pago_1 = total * self.adelanto / 100
self.pago_2 = total - self.pago_1
def generar_recibo(self):
"""Genera los recibos para entregar al cliente"""
self.calcular_pagos()
txt = leer_archivo(TEMPLATE_RECIBO)
diccionario = dict(titulo=self.titulo,
numero=self.numero_presupuesto,
ciudad=CIUDAD,
cliente=self.cliente,
moneda=MONEDA_DENOMINACION,
pago_1=self.pago_1,
nombre=Presupuesto.encabezado_nombre,
web=Presupuesto.encabezado_web,
pago_2=self.pago_2)
txt = Template(txt).safe_substitute(diccionario)
self.guardar_recibo(txt)
def guardar_recibo(self, contenido):
"""Guarda el recibo generado en la carpeta recibos
Argumentos:
contenido -- template renderizado
"""
filename = 'recibos/' + str(self.numero_presupuesto) + '.txt'
recibo = open(filename, 'w')
recibo.write(contenido)
recibo.close()
RUN.PY
Este archivo, será el que desde ahora en más ejecutemos de la línea de comandos en lugar de presupuesto.py.
Veremos como, pasándole un parámetro determinado por línea de comando, se encargará de ejecutar uno u otro
módulo (Presupuesto o PresupuestoConRecibo).
# -*- coding: utf-8 *-*
import sys
from presupuesto import Presupuesto
from recibo import PresupuestoConRecibo
modelo = sys.argv[1]
if modelo == 'basico':
presupuesto = Presupuesto()
elif modelo == 'recibo':
presupuesto = PresupuestoConRecibo()
else:
print "Argumentos no válidos"
ESE EXTRAÑO ARCHIVO CON EXTENSIÓN .NJA
El Capítulo VI de la Guía Python, lo he desarrollado con el IDE Open Source,NINJA-IDE.
.
El archivo Guia_Python.nja será opcionalmente necesario, si deseas utilizar Ninja-IDE para esta etapa del
proyecto. El archivo .nja te evitará configurar el proyecto, pero es opcional su descarga.
ARCHIVOS MODIFICADOS
presupuesto.py
- Código estandarizado según PEP 8
- Se elimina además, la instancia a Presupuesto incorporándola en run.py
- Se limpia el método __init__()
- Ahora, Presupuesto hereda de object (ver explicación más adelante)
constantes.py
- Incorpora nuevas constantes
RAZONAMIENTO LÓGICO DE LA HERENCIA
Como bien se comentó al principio, la herencia es una de las características
que define al paradigma de la Programación Orientada a Objetos (POO), estableciendo la forma en la cual, dos
o más clases se relacionan entre sí.
Cuando una clase hereda de otra, ésta, adquiere de forma automática, los métodos y atributos de la clase de la
cual hereda.
Existe una lógica relacional en la herencia de clases. Una clase no debe heredar al azar de otra, sino que debe
existir una relación real.
Llevado a un ejemplo de la vida diaria, podríamos tener una clase principal llamada Persona, que sea heredada
por la clase Hombre y por la clase Mujer. Hombre y Mujer, tendrían los mismos atributos que Persona
(extremidades superiores, inferiores, órganos vitales), pero cada una tendría atributos propios que las distinguen
entre sí y a la vez extienden a Persona (órganos reproductores, genes). De la misma manera, compartirían los
mismas métodos que Persona (caminar, correr, comer), pero cada una, tendrían sus propios métodos distintivos
(no, no me pidan ejemplos, usen la imaginación!!!!).
Sin embargo, no sería relacionalmente lógico, que Perro herede de persona. Si bien puede tener atributos y
métodos que a simple vista resultan similares (correr, comer, caminar) no es una clase de Persona sino de
Animal.
LA HERENCIA EN PYTHON
En Python, para indicar que una clase hereda de otra, se utiliza la siguiente sintaxis:
class NombreDeLaClaseHija(NombreDeLaClaseMadre):
Cuando una clase es principal (una clase madre), debe heredar de object:
class Presupuesto(object):
Nuestro nuevo módulo recibo.py, hereda todos los atributos y métodos de presupuesto:
class PresupuestoConRecibo(Presupuesto):
Además, de poder definir métodos y atributos propios que extenderán las características de Presupuesto.
ACCEDIENDO A MÉTODOS Y ATRIBUTOS
Para acceder a las propiedades de clase, es decir, aquellos atributos definidos en la propia clase ANTES de ser
instanciada, se utiliza:
NombreDeLaClase.nombre_del_atributo
Sin embargo, si se desea acceder a propiedades del objeto, es decir, a aquellos atributos definidos LUEGO de
crear una instancia de la clase, se utiliza:
self.nombre_del_atributo
Es decir, que dado el siguiente código:
class ClaseMadre(object):
atributo_de_clase = 'valor'
def __init__(self):
self.metodo()
def metodo(self):
self.atributo_del_objeto = 'otro valor'
Si heredo esta clase, por otra:
class ClaseHija(ClaseMadre):
Para acceder a “atributo_de_la_clase” dentro de ClaseHija, tendré que hacerlo mediante:
print ClaseMadre.atributo_de_la_clase
Aunque también es posible, acceder mediante self:
print self.atributo_de_la_clase
Mientras que para acceder a “atributo_del_objeto”, primero se debe haber ejecutado el método que define dicha
propiedad, es decir metodo():
class ClaseHija(ClaseMadre):
def __init__(self):
ClaseMadre.__init__(self)
Para luego acceder a dicho atributo, mediante self:
print self.atributo_del_objeto
Sin embargo, podré acceder a cualquier método heredado, utilizando self directamente:
class ClaseMadre(object):
atributo_de_clase = 'valor'
def __init__(self):
self.metodo()
def metodo(self):
self.atributo_del_objeto = 'otro valor'
def segundo_metodo(self):
print 'Hola Mundo'
class ClaseHija(ClaseMadre):
def __init__(self):
ClaseMadre.__init__(self)
self.otro_metodo()
def otro_metodo(self):
print ClaseMadre.atributo_de_clase
print self.atributo_del_objeto
self.segundo_metodo()
PASANDO PARÁMETROS A UN ARCHIVO .PY POR LÍNEA
DE COMANDOS
Desde ahora, para ejecutar nuestro programa, ya no tendremos que hacer python presupuesto.py, sino:
python run.py argumento
Donde argumento podrá ser: basico, quien ejecutará el módulo Presupuesto de la misma forma que en el
Capítulo V o recibo, el cual ejecutará el Presupuesto extendido con la generación de recibos de pago.
Si ejecutas por línea de comandos:
presupuesto run.py recibo
Al finalizar, en la nueva carpeta “recibos” se habrá generado un TXT con el mismo número que el presupuesto
creado, conteniendo dos recibos de pago para imprimir.
En cambios, si ejecutas:
presupuesto run.py basico
No habrá diferencia con lo que hemos hecho hasta el capítulo anterior.
CAPTURANDO ARGUMENTOS ENVIADOS POR LÍNEA DE
COMANDOS (EN RUN.PY)
import sys
modelo = sys.argv[1]
sys es un módulo estándar de Python que provee de funciones específicas del sistema (ampliar información).
argv recoge una lista de parámetros que son pasados por línea de comandos cuanso se ejecuta mediante python
archivo.py argumentos.
El primer elemento de la lista argv, es decir argv[0] es el nombre del archivo. En nuestro código, accedemos
directamente al segundo elemento de la lista:sys.argv[1] el cuál nos dirá qué opción hemos elegido. Si optamos
por básico, crearemos una instancia de Presupuesto()
if modelo == 'basico':
presupuesto = Presupuesto()
En cambio, si hemos indicado “recibo” obtendremos una instancia de PresupuestoConRecibo()
elif modelo == 'recibo':
presupuesto = PresupuestoConRecibo()
De lo contrario, se imprimirá un mensaje de error:
else:
print "Argumentos no válidos"
Ver más información sobre paso y captura de argumentos por línea de comando.
EL DESAFÍO DE HOY
Nos estamos poniendo cada vez más exigentes con nuestro código. En el capítulo anterior, nos tocó hacer
un refactoring para estandarizar el código con la normativa de la PEP 8.
En el capítulo de hoy, el reto es doble.
DESAFÍO #1:
Prueba a ejecutar el módulo run.py sin pasar ningún argumento:
python run.py
¿Te animas a solucionar el inconveniente con la ayuda del tutorial oficial de Python?
DESAFÍO #2
El nuevo módulo recibo.py posee un método para guardar el recibo generado, muy similar al método que utiliza
el módulo Presupuesto para guardar el presupuesto en un archivo HTML. Muchas líneas de código se repiten, lo
cual, incurre en una redundancia innecesaria que puede ser evitada. ¿Qué ideas se te ocurren para solucionar
este código redundante? No es necesario que escribas código. El objetivo es que entre todos razonemos y
hagamos una lluvia de ideas que nos ponga en práctica.
TESTEANDO CÓDIGO CON DOCTEST EN LOS
COMENTARIOS
Ya estamos llegando al final de la guía Python!
En el capítulo de hoy, “atacaremos” con una técnica de programación indispensable, que nos dará una gran
ventaja: evitar “romper” el código con la incorporación de uno nuevo y prevenir bugs. Y ¿de qué técnica
hablamos? Unit Testing o Test Unitarios.
TEST UNITARIOS
Los test unitarios representan un mecanismo indispensable, para probar el funcionamiento individual, de cada
parte del código, previniendo que el agregado de nuevo código, haga fallar el existente.
CHANGELOG DEL CAPÍTULO VII
Antes de comenzar, será necesario descargar los archivos actualizados del capítulo anterior.
A continuación un resumen de los cambios que veremos:
capitulo6/constantes.py
capitulo6/helpers.py
Nueva constante no pública
Agregados test con doctest
nueva función para guardar archivos
capitulo6/presupuesto.py
Refactoring: guardar_presupuesto()
utiliza función genérica para guardar
capitulo6/recibo.py
Refactoring: guardar_recibo()
utiliza función genérica para guardar
capitulo6/run.py
refactoring: validación argumentos recibidos
capitulo6/test/
carpeta que contiene los test del capítulo
Hoy, centraremos nuestra atención, haciendo énfasis en los cambios realizados al archivo helpers.py.
Comencemos!
DOCTEST
doctest es un módulo nativo de Python, que busca en los comentarios de nuestro código, fragmentos que se
vean como sesiones del intérprete interactivo de Python, y procede a ejecutar dichos fragmentos, verificando
que resulten como se le ha indicado.
Esto significa, que importando el módulo doctest, éste, buscará en los comentarios de nuestro código, todo
fragmento que represente al interprete interactivo, para luego ejecutarlo. Por ejemplo:
import doctest
def sumar_dos_numeros(a, b):
"""Suma dos números y retorna su resultado
Argumentos:
a -- primer sumando
b -- segundo sumando
Test:
>>> sumar_dos_numeros(25, 10)
35
>>> sumar_dos_numeros(30, 20)
50
"""
resultado = a + b
print a + b
if __name__ == "__main__":
doctest.testmod()
Si vemos el texto debajo de “Test:”, luce como el intérprete interactivo.
Aquí estoy invocando a la función:
>>> sumar_dos_numeros(25, 10)
Aquí, estoy “simulando” el resultado que arrojaría en el intérprete interactivo. Esto, será interpretado por
doctest, como “el resultado esperado”:
35
Y finalmente, verifico que el módulo esté siendo ejecutado como script (no importado), de ser así, doy la orden
a doctest de ejecutar el script en modo “test”:
if __name__ == "__main__":
doctest.testmod()
COLOCANDO LOS “DOCTEST” EN UN ARCHIVO
INDEPENDIENTE
En nuestro ejemplo (archivo helpers.py), sin embargo, nos encontramos con estas líneas:
74
75
if __name__ == "__main__":
doctest.testfile('tests/helpers.txt')
En este caso, lo que estamos haciendo, es indicar a doctest, que nuestras pruebas se encuentran en un archivo a
parte: tests/helpers.txt Si abrimos este archivo, podremos ver todos los test:
1
>>> from helpers import leer_archivo, crear_archivo,
mostrar_texto
2
3
Probando leer_archivo()
4
5
>>> leer_archivo('')
6
'Error'
7
8
>>> leer_archivo('tests/archivo_de_prueba.txt')
9
'Archivo de Prueba\nHola Mundo\n'
10
11
Probando crear_archivo()
12
13
>>> crear_archivo('', 'contenido')
14
'Error'
15
>>> crear_archivo('', '')
16
'Error'
17
>>> crear_archivo('tests/archivo_de_prueba.txt', '')
18
'Error'
19
>>> crear_archivo('tests/archivo_de_prueba.txt', 'Archivo de Prueba\nHola Mundo\n')
20
21
22
Probando mostrar_texto()
23
24
>>> mostrar_texto('Hola Mundo')
25
Hola Mundo
26
>>> mostrar_texto()
25
Si te fijas las líneas resaltadas en gris, podrás ver las llamadas a los métodos que estamos testeando. Mientras
que las resaltadas en negro, simulan el resultando esperado.
El texto que no aparece resaltado, es interpretado como parte de los comentarios, exceptuando la línea 1, que se
encarga de importar los métodos a ser testeados.
EJECUTANDO LOS TEST
Una vez que el código fuente cuenta con los correspondientes test, es hora de correrlos. Para ello, en la línea de
comandos, escribiremos:
python nombre_del_modulo_a_testear.py -v
En nuestro caso, navegaremos hasta la carpeta capitulo6 y escribiremos
python helpers.py -v
Cuando ejecutemos los test, veremos una salida similar a la siguiente:
Trying:
from helpers import leer_archivo, crear_archivo, mostrar_texto
Expecting nothing
ok
Trying:
leer_archivo('')
Expecting:
'Error'
ok
Trying:
leer_archivo('tests/archivo_de_prueba.txt')
Expecting:
'Archivo de Prueba\nHola Mundo\n'
ok
Trying:
crear_archivo('', 'contenido')
Expecting:
'Error'
ok
Trying:
crear_archivo('', '')
Expecting:
'Error'
ok
Trying:
crear_archivo('tests/archivo_de_prueba.txt', '')
Expecting:
'Error'
ok
Trying:
crear_archivo('tests/archivo_de_prueba.txt', 'Archivo de Prueba\nHola
Mundo\n')
Expecting nothing
ok
Trying:
mostrar_texto('Hola Mundo')
Expecting:
Hola Mundo
ok
Trying:
mostrar_texto()
Expecting nothing
ok
1 items passed all tests:
9 tests in helpers.txt
9 tests in 1 items.
9 passed and 0 failed.
Test passed.
En lo anterior, “Trying” nos describe el código que se está ejecutando, mientras que “Expecting”, el resultado
esperado. Si todo sale bien, concluirá el bloque indicando “ok”.
Al final del test, se puede acceder al reporte completo:
1 items passed all tests:
9 tests in helpers.txt
9 tests in 1 items.
9 passed and 0 failed.
Test passed.
SOLUCIÓN DEL DESAFÍO DEL CAPÍTULO ANTERIOR
En el segundo desafío del capítulo anterior, la propuesta era pensar como podría evitarse la redundancia de
código, en los métodos encargados de guardar los recibos y los presupuestos en formato HTML.
Siguiendo la línea inicial, de convertir en “ayudantes genéricos” aquellos métodos sin relación directa con el
compartamiento del objeto en sí mismo, se creó un helper, para guardar dichos archivos:
47
48
def crear_archivo(ruta, contenido):
"""crea un archivo en la ruta pasada con el contenido indicado
49
50
Argumentos:
51
ruta -- ruta al archivo. Ej. carpeta/archivo.txt
52
contenido -- template renderizado
53
54
"""
55
if not ruta or not contenido:
56
57
return 'Error'
else:
58
archivo = open(ruta, 'w')
59
archivo.write(contenido)
60
archivo.close()
ANEXO DE MATERIAL DE LECTURA COMPLEMENTARIO
El desafío de hoy, no dependerá de “resolver” un problema ni de encontrar ningún tipo de solución puntual. El
reto de hoy, consiste en desafiarte a ti mismo:
Nunca terminas de aprender y jamás es suficiente lo que te enseñan.
Programar, no se limita a conocer la sintaxis y funcionamiento de un lenguaje de programación. Siempre podrás
comenzar por una guía de aprendizaje, para conocer los caminos que puedes seguir, para convertirte en un
verdadero profesional en determinada materia. Pero eso, no debe significarlo todo.
Solo lograrás ser un experto, en el momento en el que descubras, que jamás es suficiente lo que
conoces y que el conocimiento no te otorga sabiduría.
Tienes una alternativa: no limitar tus recursos al mero conocimiento. No acotar tu carrera profesional al
conocer sobre un lenguaje de programación.
De tu voluntad, dependerá el nivel que alcances, y de tu pasión, el superarte cada día.
A continuación, encontrarás un listado de recurso, que no puedes dejar de leer. La gran mayoría se encuentran
en español. Las identificadas como “indispensables”, te recomiendo que hagas todo lo posible por leerlas. Las
“recomendadas”, significa que “sería una buena idea” leerlas. Y las “complementarias”, dependerán de tu
curiosidad.
EL CAMINO CORRECTO…
Recurre a la razón, para asimilar lo que lees. Pero recurre a tus emociones, para saber si lo que haces,
realmente te apasiona. Solo así, sabrás que estás siguiendo el camino correcto.
¡Disfruta la lectura y apasiónate practicando!
LECTURA INDISPENSABLE (ES)
Guía de aprendizaje de Python por Guido van Rossum (creador de Python)
Excelente material de referencias básicas del lenguaje.
Inmersión en Python (Dive into Python) por Mark Pilgrim
Excelente material para conocer a fondo el lenguaje y su aplicación en la programación orientada a objetos.
Python no muerde por Roberto Alsina
Excelente libro para aprender a razonar más allá del lenguaje.
LECTURAS RECOMENDADAS (EN ESPAÑOL)
Tutorial Django por @cibernatural
Una muy buena guía para entender el funcionamiento del framework Django, ideal para crear aplicaciones
Python Web based.
Tutorial de PyGTK por John Finlay
Tutorial oficial de PyGTK, una suite de módulos Python para crear aplicaciones de escritorio con interfaces
gráficas GTK.
LECTURAS COMPLEMENTARIAS (EN ESPAÑOL)
Aprenda a Pensar como un Programador con Python por Allen Downey, Jeffrey Elkner y Chris Meyers
Un libro extenso, para leer con tiempo y calma. Muy buen material para utilizar como referencia del lenguaje.
Los últimos capítulos, hacen mayor énfasis en la programación orientada a objetos.
LECTURAS RECOMENDADAS (EN)
MySQLdb User’s Guide por Andy Dustman
Guía oficial de MySQLdb, interface para trabajar con bases de datos MySQL desde Python.
Python doctest por Guido Van Rossum
Documentación oficial de Python doctest
The PyQT Tutorial por ZetCode
Tutorial sobre PyQT para crear aplicaciones de escritorio con interfaces gráficas (un especial
agradecimiento Jorge Courbis por pasarme el link)
QUÉ TE GUSTARÍA APRENDER EN EL
PRÓXIMO CAPÍTULO?
El siguiente capítulo de la Guía Python te trae un desafío por adelantado. Esta vez, el tema lo elegirás tú y será
sometido a votación, mediante una encuesta.
A continuación, encontrarás una brevísima introducción a cada uno de los temas que puedes elegir en la
votación. Esta introducción te servirá para poder decidirte por uno con mayor facilidad. Recuerda que solo
puedes elegir una opción:
1. ACCESO A BASES DE DATOS CON MYSQLDB
MySQLdb es una interface para MySQL que nos permite manejar bases de datos desde Python.
Aprenderemos a conectarnos a una base de datos, leer los registros de una tabla, insertar nuevos y editar
los existentes.
¿Qué necesitarás para seguir este capítulo?
Los siguientes puntos serán necesarios para entender el capítulo, ya que no serán explicados:
o
o
Tener MySQL instalado
Conocer mínimamente la sintaxis del lenguaje SQL
2. TDD EN PYTHON CON UNITTEST
TDD (Desarrollo guiado por pruebas) y Test-First Programming, es una técnica de programación que
consiste en escribir tests antes de generar nuestro código. Esta técnica de programación, es una de las
más complejas pero es la única que garantiza un código limpio, legible, estable y libre de errores.
Aprenderemos conceptos básicos sobre TDD, el algoritmo de TDD, cómo escribir tests antes de
programar y ejecutar test unitarios.
¿Qué necesitarás para seguir este capítulo?
Los siguientes puntos serán necesarios para entender el capítulo, ya que no serán explicados:
o
Ganas de programar profesionalmente y estudiar algo que cuesta aprender solo.
3. INTERFACES GRÁFICAS PARA APLICACIONES DE
ESCRITORIO EN PYTHON
Las interfaces gráficas son aquellas que te permitirán desarrollar una GUI para las aplicaciones de
escritorio desarrolladas en Python, es decir, que no correrán por consola sino, en modo gráfico. Veremos
cómo mostrar texto, agregar un menú sencillo, un campo de texto y botones con WxPython y PyQT.
¿Qué necesitarás para seguir este capítulo?
Los siguientes puntos serán necesarios para entender el capítulo, ya que no serán explicados:
o
¿Nada? ¿Ganas de… ver una introducción a interfaces gráficas?
¿CÓMO VOTAR POR EL PRÓXIMO TEMA?
Primero, asegúrate de haber leído los detalles de cada tema en el punto anterior.
Luego, dirígete a la página de Facebook de Maestros del Web y vota por el tema que quieres aprender.
INTERFACES GRÁFICAS CON WXPYTHON
A pedido “popular”, pues, ha sido votado por una amplia mayoría y, a solo efecto de cumplir la voluntad de
“las masas”, en el capítulo de hoy veremos un ejemplo de un script Python que ¡se ejecuta en modo gráfico!
OMFG! ¡En modo… GRÁFICO! Jesus! Tocaremos el cielo con las manos! []
HOLA MUNDO “EXTENDED”
Haremos un “Hola Mundo” con wxPython, pero con algo más que un simple “Hola Mundo”.
Nuestro script, será similar a un editor de textos (como el Notepad de Güindous), con la diferencia de que por
defecto, nos mostrará una plantilla decarta comercial y la guardará automáticamente en una carpeta destinada a
almacenar todas nuestras cartas.
Lo que haremos se verá así:
Para hacer este ejemplo, me inspiré en la Guía oficial de Primeros Pasos con wxPython. De esta forma, será
más simple entender la documentación oficial.
INSTALACIÓN DE WXPYTHON
El primer paso, será instalar wxPython.
En Linux será necesario el paquete: python-wxgtk2.8. En aquellos basados enDebian, basta con hacer:
sudo apt-get install python-wxgtk2.8
En Gentoo:
emerge wxpython
Para RedHat, Fedora, Mandriva y compañía, pueden buscar los rpm de la versión 2.8.12.0 en este
enlace www.rpmfind.net/…search.php?query=wxPython o instalarlo con yum (como root):
yum install wxPython
Para Windows y Mac OS
Descargar instalador en www.wxpython.org/download.php
Dudas/problemas sobre instalación:
Visitarhttp://wiki.wxpython.org/How%20to%20install%20wxPython
INTRODUCCIÓN
wxPython es una suite de librerías de interfaces gráficas para Python (programadas en C++), destinadas a crear
GUIs de forma simple. Funciona como un módulo más de Python, debiendo ser importado al igual que
cualquier otro.
Cómo principales características de wxPython pueden citarse:
1. Portabilidad: Windows, Linux y Mac OS X
2. Apariencia: Nativa en todos los S.O.
3. Licencia: LGPL
ARCHIVOS DEL EJEMPLO
Para nuestro creador de cartas comerciales necesitaremos 2 archivos:

constants.py

editor.py
Descargar ambos archivos, guardarlos en una carpeta y, dentro de esta, crear un sub-directorio llamado
cartas. En este sub-directorio, se guardarán automáticamente las cartas que vayamos creando.
Para correrlo, simplemente haremos python editor.py y se ejecutará el script en modo gráfico.
UTILIZANDO WXPYTHON EN 6 PASOS
1. Importar wxPython:
import wx
2. Crear una clase extendida de wx.Frame:
class MiClase(wx.Frame)
3. Crear los métodos necesarios de la clase. Aquí, además de definir algoritmos propios, es donde se harán las
llamadas a los métodos de wxPython y wxWidgets (ver más adelante)
4. Finalmente, crear una instancia de wx.App:
app = wx.App(False)
5. Otra instancia de nuestra clase:
ventana = MiClase(None, “Título de la ventana”)
6. Llamar a MainLoop del objeto app para mantener activo el programa:
app.MainLoop()
CREANDO MÉTODOS DE LA CLASE
Los métodos de la clase, lógicamente dependerán de lo que queramos que nuestro programa realice. Estos
métodos variarán de acuerdo a cada programa. La“fórmula” a seguir, será recomendable que se desarrolle de la
siguiente manera:
DEFINIR EL MÉTODO __INIT__
Definir el __init__ de la clase, cuyo parámetro sea (además de self), parent. Esto garantizará hacer siempre una
referencia al elemento “parent” (padre) del objeto que se esté instanciando.
En nuestro caso, cuando instanciamos al editor, como ventana “padre” le estamos indicando “None” (ninguna),
ya que es la primera ventana que estamos instanciando:
frame = Editor(None, APP_TITLE)
En este ejemplo, además, hay un segundo parámetro que corresponde al título de la ventana. Pero éste,
podríamos haberlo evitado, si lo definíamos directamente en el __init__. No lo hicimos, para poder extender el
día de mañana, este ejemplo, instanciando otras ventanas con un nombre diferente.
En el __init__, además, se debe inicializar al propio wx.Frame:
wx.Frame.__init__(self, parent, title=title, size=(800, 600))
Como se puede ver, a wx.Frame se le pueden pasar diversos parámetros. En nuestro caso, un título de ventana y
las medidas de la misma (ancho, alto).
Es importante además, siempre llamar al método Show(), ya que de lo contrario, la ventana nunca se
visualizará:
self.Show(True)
Por otro lado, en el método __init__ deben establecerse todas las propiedades de clase y llamar a aquellos
métodos que se encarguen de generar toda la GUI.
INCRUSTANDO WIDGETS
wxPython, utiliza wxWidgets. Estos “Widgets” serán todos aquellos elementos que se irán incrustando en el
contenedor (frame). Una lista completa de los Widgets implementados por wxPython, puede econtrarse
enhttp://docs.wxwidgets.org/2.8/wx_wxpython.html#wxpclasses.
Para utilizar cualquiera de estos Widgets, deben ser considerados como métodos de wx, simplemente
utilizando la sintaxis wx.Widget(parametros).
WIDGETS UTILIZADOS EN NUESTRO CREADOR DE CARTAS COMERCIALES
En nuestro script, los widgets utilizados son:
wx.TextCtrl()
Control de entrada de texto que permite mostrar y editar su contenido. Puede ser de una sola línea o multilínea.
Con el parámetro value se define el contenido por defecto, mientras que styledebe ser definido con
las constantes de estilo del control.
wx.Menu()
Un menú ya sea como lista desplegable o ventana emergente (pop-up) que permite seleccionar un elemento
capturando el evento y actuando en consecuencia.
wx.MenuBar()
Una barra conteniendo una serie de menús accesibles desde la parte superior de la ventana.
wx.MessageDialog()
Un cuadro de diálogo que muestra un mensaje de una o varias líneas, con botones de opción como Aceptar, Sí,
No y Cancelar. Este Widget recibe como parámetros:parent la ventana “padre”, mensaje, titulo y estilo.
Ver lista completa de wxWidgets implementados por wxPython.
LLAMANDO A LOS MÉTODOS HEREDADOS DE WX.FRAME
wx.Frame es una clase del módulo wx, que hereda de otras y por lo tanto, pone a nuestra disposición, una larga
lista de métodos, a los cuáles accederemos simplemente mediante self.Metodo().
Los métodos que utilizamos en nuestro código son:
wx.Frame.CreateStatusBar()
Genera la barra de estado de la ventana.
wx.Frame.SetMenuBar()
Se encarga de crear la barra de menú con todos los wxWidgets configurados.
wx.EvtHandler.Bind()
Captura un evento y lo enlaza a un controlador de eventos.
wx.SizerFlags.Centre()
Centra la ventana en el medio de la pantalla.
wx.Window.Show()
Muestra (u oculta) la ventana.
wx.Window.Close()
Cierra la ventana.
CONCLUSIÓN
Para utilizar cualquier interfaz gráfica en Python, solo es necesario:
1. Saber Python (para programar algo y no solo mostrar ventanas con menús, textos y botones que no hagan nada)
2. Entender la sintaxis y semántica de la programación orientada a objetos(para entender como funcionan las
librerías gráficas)
3. Tener a mano la guía de referencias de la interface gráfica a utilizar(simplemente, para saber “el nombre” de
los métodos y objetos a los cuales recurrir, para “mostrar gráficamente” aquello que tuvimos que programar en
Python)
FINALIZANDO CON PYTHON Y MYSQL
Con el capítulo de hoy, llegamos al final de esta guía. Considero que hemos aprendido muchísimas cosas de
gran importancia y que no solo se han enfocado en el dominio de un lenguaje (como Python, en este caso), sino
que además, introdujeron buenas prácticas y conceptos de la programación en general, sus diversas técnicas y
paradigmas.
En este último “episodio”, nos avocaremos a un tema particular, el cual es prácticamente imposible evitar ser
abarcado en el desarrollo de un sistema informático. Me refiero a la interacción con bases de datos.
¡Comencemos!
TRABAJANDO CON BASES DE DATOS MYSQL EN
PYTHON
No abordaremos aquí, conocimientos básicos sobre MySQL, uso, instalación ni configuración. Sin perjuicio de
lo anterior, haré una breve introducción.
STRUCTURED QUERY LANGUAGE (SQL)
SQL es el lenguaje de consulta estructurado utilizado para el acceso a bases de datos relacionales. Si bien
SQL como lenguaje, posee ciertos estándares, el lenguaje de consulta en sí, varía para cada base de datos en
particular, siendo el tratado en este ejemplo, el correspondiente a MySQL.
MYSQL
MySQL es un sistema de gestión de bases de datos relacionales, libre y que puede ser instalado en múltiples
plataformas.
Para seguir el capítulo de esta guía, será necesario instalar MySQL. Para ello los invito a quienes no posean
MySQL instalado, que visiten el sitio de descargas de MySQL y la documentación de MySQL.
MYSQLDB
MySQLdb es una interfaz para trabajar con bases de datos MySQL desde Python. El capítulo Nº7 de esta
guía, se ha indicado como lectura recomendada, el Manual oficial de MySQLdb el cual sugiero que se lea tras
finalizar este capítulo.
INSTALACIÓN DE MYSQLDB
Para interactuar desde Python con MySQL a través de MySQLdb, es necesario instalar dicho módulo. El
nombre del paquete es python-mysqldb (por si desean instalarlo desde los repositorios), o sino, pueden
descargar un tarball desdehttp://sourceforge.net/projects/mysql-python/. Allí mismo, encontrarán el
archivoREADME con toda la información para instalarlo.
INTRODUCCIÓN A BASES DE DATOS CON PYTHON
En el caso particular de Python, el acceso a bases de datos se encuentra definido a modo de estándar en las
especificaciones de DB-API (por curiosidad, puedes visitar Python Database API specification).
Esto significa, que para utilizar cualquier base de datos, siempre se deberán seguir los mismos pasos:
1. Importar el módulo de conexión (en nuestro caso, utilizaremos MySQLdb)
import MySQLdb
2. Conectarse a la base de datos
db_host = 'localhost'
usuario = 'root'
clave = 'clave'
base_de_datos = 'mi_basededatos'
db = MySQLdb.connect(host=db_host, user=usuario, passwd=clave,
db=base_de_datos)
3. Abrir un cursor
cursor = db.cursor()
4. Ejecutar una consulta
mi_query = "SELECT campo FROM tabla WHERE campo='valor' ORDER BY campo"
cursor.execute(mi_query)
5. Si se está agregando, editando o eliminando un registro: hacer un commit a la base de datos
db.commit()
6. Si se están leyendo datos: obtener todos los registros hallados
cursor.fetchall()
u obtener solo el primero:
cursor.fetchone()
7. Cerrar el cursor abierto
cursor.close()
MANOS A LA OBRA!
Hecha esta introducción, estamos en condiciones de arrancar.
CREAR LA BASE DE DATOS QUE UTILIZAREMOS
Lo primero que debemos hacer, es crear una nueva base de datos (o utilizar una existente) y ejecutar el query
(consulta) que creará la tabla que utilizaremos.
Para ello, abriendo una terminal accedes a MySQL escribiendo:
mysql -u nombre_de_usuario_de_mysql -p contraseña_de_mysql
Lógicamente, modificarás nombre_de_usuario_de_mysql por tu nombre de usuario
y contraseña_de_mysql por tu clave. Al pulsar la tecla “enter”, verásmysql>
Una vez allí, si deseas crear una nueva base de datos, escribe:
create database nombre_de_la_nueva_base_de_datos;
Luego, indicamos a MySQL la base de datos que utilizaremos, escribiendo:
use nombre_de_tu_base_de_datos;
Habiendo indicado la base de datos a utilizar, ya estás en condiciones de ejecutar la consulta que creará la tabla.
Para ejecutar la consulta, copia el siguiente query y luego pulsa la tecla enter:
CREATE TABLE IF NOT EXISTS paises (
id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
pais VARCHAR(125) NOT NULL,
abbr CHAR(2) NOT NULL
) ENGINE=InnoDB;
DESCARGAR ARCHIVOS
El ejemplo de este capítulo, corresponde a un ABM de países con arquitectura MVC (modelo-vistacontrolador). El mismo, cuenta con los siguientes archivos:




db_conn.py (Capa de abstracción para la base de datos)
pais.py (Modelo)
pais_view.py (Vista)
pais_controller.py (Controlador)
Para ejecutar este ejemplo, llamar a pais_controller.py:
python pais_controller.py
Como podrán observar, este controlador es quien recibe las peticiones que hagamos, encargándose de realizar
los cambios necesarios al modelo (la clase Pais), llamar a este modelo, y luego, enviar los datos necesarios a la
vista, para que ésta, finalmente, sea quien nos muestre esos datos en pantalla.
Saber más sobre el patrón arquitectónico MVC
BALANCE FINAL DE LA GUÍA
Así es que llegamos al final de esta guía.
Queda muchísimo por aprender y sobre todo, son abundantes los consejos que podríamos tomar para crecer
profesionalmente. Creo, que quienes hayan seguido la guía paso a paso y participado activa o pasivamente de
los comentarios de cada capítulo, habrán sabido comprender las bases necesarias para no ser “un programador
más”.
Como docente, me llevo conmigo la satisfacción de, una vez más, no haberme reservado lo que aprendí a lo
largo de mi carrera, como un secreto bajo llave, sino, el haberlo compartido con el solo objeto de ser
aprovechado por quienes se sintiesen interesados, curiosos, motivados, etc.
Como redactora de esta guía, quedan almacenadas en mi memoria, un cúmulo de experiencias aprovechables,
producto de la interacción humana en un medio de comunicación tan particular como lo es Internet.
Y como persona, me llevo la indudable realidad sobre una educación general en decadencia, que quisiera haber
evitado conocer, pero que al mismo tiempo, me abre los ojos a un camino alternativo para cumplir mis
objetivos.
Finalmente, tras todo el balance anterior y, dadas las fechas que se aproximan, quisiera enviar un gran saludo a
judíos y cristianos, ya que esta noche (25 de Kislev) comienza ‫ ָּהכֻנֲח‬, pues les deseo ¡MUY FELIZ JANUCÁ! a
toda la cole y para este finde del 24 y 25 de diciembre, ¡MUY FELIZ NAVIDAD! a todos los cristianos!
Y por supuesto, para todos, un ¡EXCELENTE 2012!
DOS HÉROES DE PYTHON NOS CUENTAN SUS
EXPERIENCIAS
Dentro de la diversidad de lenguajes que existen, Python se ha caracterizado por su facilidad de aprendizaje y
flexibilidad en el desarrollo de aplicaciones y web. Permite a programadores principiantes entender rápidamente
su sintaxis y a experimentados la simplificación de lineas de código.
El crecimiento de su comunidad va en aumento al igual que la presencia de
aplicaciones y desarrollo web. En una entrevista Julián Amaya, co-fundador de Monoku, nos confiesa estar
enamorado de Python desde hace 3 años y nos comenta algunas razones del porqué está enfocado en éste
lenguaje:
Una de las cosas chévere de Python es que se usa en muchas cosas, como lenguaje es muy flexible
para trabajar aplicaciones nativas y juegos.
Julián menciona también que algo que le gusta de Python es que permite escribir frameworks
como Django “que son muy bonitos y permiten trabajar muy fácil”. Opina que el que algunas aplicaciones
trabajen Python en el Frontend en vez de Javascript y otros lenguajes que trabajan cosas que se parecen a
Python muestra que “Python está aquí para quedarse y para hacer muchas cosas”.
DESARROLLEMOS PROPUESTAS
Alvaro Martínez desarrolla en Python y es moderador del foro en Foros del Webdesde el 2005, nos comenta que
en principio se interesó mucho por HTML porque “lo veía como algo con lo que se podían lograr cosas
interesantes”. También visitaba los foros de php y css, sin embargo, desde hace tres años participa activamente
en el foro de Python.
Alvaro comenta que es importante que la comunidad que programe en Python
evolucione, que hayan nuevas propuestas para asegurar su avance y desarrollo sino, por ejemplo, visitar un foro
se vuelve aburrido cuando no hay más que las mismas preguntas por parte de los usuarios.
He mencionado algunas características positivas que nos hacen considerar aprender Python a quienes no saben
y reafirmar porqué lo hemos elegido para desarrollar, a pesar de ellos el lenguaje tiene aspectos negativos en los
cuales ambos desarrolladores están de acuerdo: Python tiene una comunidad grande pero carece de soporte,
como menciona Julián no es tan fácil encontrar un servidor para Python como lo es para PHP.
¿CÓMO PUEDO APRENDER PYTHON?
Python es bastante intuitivo, ambos desarrolladores comentan que aprendieron practicando, realizando
proyectos con aspectos básicos y poco a poco mejorarlos. Si quieres aprender Python y no conoces ningun
lenguaje de programación Julian recomienda el libro Learn Python: The hard way que te lleva de lo básico a lo
avanzado. Si tienes ya experiencia en algún lenguaje que no sea Python, “crea un proyecto” es fácil y aprendes
mientras lo desarrollas.
GUÍA PYTHON
En Maestros del web publicamos hace algunos meses el primer capítulo de la guía Python y aún estamos en
proceso de agregar más capítulos de este lenguaje. En próximas entregas publicaremos más capítulos con
autoría de Alvaro Martínez.
CÓMO SE UTILIZA PYTHON Y DJANGO EN
MEJORANDO.LA
Seguramente conoces Mejorando.la, es el proyecto web de Maestros y Cristalab. Comenzó como programa
online una vez por semana y ahora se ha expandido concursos presenciales alrededor de latinoamérica y tres
conferencias con alcance mundial.
Leonidas Esteban, Renzo Avalos y Adan Sanchez son quienes hace unos meses estuvieron trabajando para
lograr el sitio que hoy se puede disfrutar al ingresar aMejorando.la. Para lograrlo hubo todo un proceso de
rediseño y reprogramación que fue posible gracias a varias herramientas y servicios que con la ayuda de Adan
Sanchez te contaré a continuación.
PYTHON Y DJANGO: LENGUAJE Y FRAMEWORK DE LA
MAGIA
“Al momento de planear el rediseño y reprogramacion de un sitio es importante identificar bien sus
elementos y componentes principales para crear los modelos necesarios y una buena estructura” es lo
primero que resaltó Adán Sanchez. Y hablando especificamente de Mejorando.la hay que destacar que el
componente principal son los videos. Desde ahí parten todas las funciones que se van agregando al sitio.
Python es el lenguaje y Django es el framework detrás del sitio de Mejorando.la. Nos cuenta Adán que gracias a
la flexibilidad que ofrece, se pudo realizar la reprogramación y rediseño del sitio en pocos días: “Digamos que
Django se ha vuelto prácticamente un estándar como framework para desarrollo ágil.”
Es especialmente recomendado para un proyecto grande desarrollado por un equipo pequeño o
cuando se trata de un sitio para entregar en pocos días.
Al consultar por qué la elección de Django y no otro, nos explicó que “Django era la solución principal, puesto
que si bien wordpress es muy flexible, en Django hay aun mas flexibilidad.”
UN FRAMEWORK CON BATERÍAS INCLUÍDAS
Django trae lo que en Python se conoce como “baterías incluidas”. Desde clases para tareas sencillas
como paginación, geolocalización, autentificación y caché hasta componentes bastante completos como
el módulo de comentarios, notificaciones, administrador, internacionalización y feeds.
Adán explica que un sitio en Django es más limpio puesto que python tiene una estructura modular, agrega que
“en otros lenguajes hay que descargarse muchos archivos y el tener tantos archivos en el directorio puede hacer
mas dificil manejar el sitio”. Para “graficar” la facilidad con la que se activan estos módulos desde el archivo de
configuración, nos mostró el código correspondiente:
INSTALLED_APPS = (
'django.contrib.auth', # funciones de autentificacion
'django.contrib.comments', # funciones para agregar comentarios a los
modelos
'django.contrib.admin', # administrador autogenerado
)
Además afirma que es muy fácil encontrar un módulo en los repositorios para aquellas funcionalidades que no
vienen incluidas en Django, un ejemplo de ello es el modulo Gravatar que se utiliza en Mejorando.la para los
avatares de los comentarios.
MODELO-VISTA-CONTROLADOR
“Un punto más a favor de Django es el patrón Modelo-Vista-Controlador que maneja, esto quiere decir que
separas tu aplicación en tres componentes”, explicó Adán. El modelo principal en este caso seria Video, en
donde un “video” tiene titulo, imagen, descripcion, comentarios.
Para entender mejor puedes ver el tercer capitulo de la guia Django y luego ver estos ejemplos:
Modelo: son los datos, en el sitio se tiene un modelo para los Videos.
Ejemplo de modelo
class Video(models.Model):
titulo = models.CharField()
slug = models.CharField()
imagen = models.ImageField()
fecha = models.DateField()
descripcion = models.TextField()
Vista: sería la presentación de este modelo, en Django entra en los templates, que reciben los datos del
controlador. Hay una plantilla para cada vista: home.html, videos.html, video.html, heredando de base.html
gracias al poderoso sistema de plantillas de Django.
Controlador: se encarga de elegir los datos que se van a mostrar y hacer el procesamiento que haga falta en
estos, validacion y condicionamientos. En Mejorando.la hay un controlador para cada página: home, archivo de
videos y página de video.
Ejemplo de controlador
def video(solicitud_http, video_slug):
video = get_object_or_404(Video, slug=video_slug)
return render_to_response('website/video.html', { 'video': video })
UNIDAD, CÓDIGO LIMPIO Y FILTROS.
La unidad que existe en toda la plataforma es una de las caracteristicas que más le gustó a Adán: “cualquier
método que defina en un modelo estará presente tanto en el controlador como en las plantillas.”
“Los modelos de Django permiten abstraer lo que tradicionalmente se haría con SQL en clases y
funciones. Esto permite tener un código más limpio.”
Por ejemplo:
SQL
SELECT titulo, descripcion, imagen FROM videos ORDER BY fecha LIMIT 10
Django
Video.objects.all().order_by(‘-fecha’)[:10]
Nos comentó también que en las plantillas de Django se hace uso de la rica cantidad de filtros que pone a
disposición, desde convertir a mayúsculas, eliminar etiquetas html y agregar saltos de línea.
Gracias a estos filtros es más fácil tener una clara separación entre datos, lógica y presentación.
SÓLO UN COMIENZO
Cómo te imaginarás el proceso de rediseño y reprogramación de un sitio como Mejorando.la no se puede
comentar y explicar en detalle en tan sólo un artículo, sin embargo en una proxima publicación con Adán te
contaremos más funcionalidades y caracteristicas que hacen de python un lenguaje fácil de aprender y
de Django el framework ideal para perfeccionistas.
SIGAMOS APRENDIENDO PYTHON
La semana pasada dos héroes de Python nos contaron sus experiencias trabajando con el lenguaje y Gissela, con
la ayuda de Adán Sánchez nos mostraron cómo se utiliza Python y Django en Mejorando.la dónde
profundizamos más en el código.
NUEVOS CAPÍTULOS DE LA GUÍA PYTHON
En busca de generar mayor contenido los capítulos de Python aún no se han convertido en eBook como
usualmente lo hacemos, Alvaro Martínez, trabaja desde hace tres años con Python, es desarrollador
y moderador del foro Python en Foros del Web ha realizado 4 capítulos para integrar en la guía desarrollando
los temas:




Conociendo a detalle las secuencias
Cadenas de texto
Expresiones regulares en Python.
Trabajando con listas, funciones y archivos.
Algunos temas que Alvaro desarrollará han sido tratados en otros capítulos por Eugenia Bahit, sin embargo, el
fin es también aclarar algunos aspectos de forma específica. No olvides revisar los capítulos anteriores de la
guía Python:






Programación Orientada a objetos en Python
Métodos y Propiedades en Python
Listas, tuplas, diccionarios y estructuras de control
Trabajando con templates, archivos y condicionales
Excepciones, helpers y refactoring
Herencia, relación entre dos o más clases



Testeando código con doctest en los comentarios
Interfaces gráficas con wxPython
Finalizando con Python y MySQL
El lanzamiento del primer capítulo es el miércoles 6 de junio y a partir de ese día se publicarán semanalmente lo
cuatro capítulos. ¿Qué te parecen los temas? ¿Le seguiste la pista a las publicaciones de Eugenia?
CONOCIENDO A FONDO EL REPOSITORIO DE
MEJORANDO.LA CREADO CON PYTHON Y
DJANGO
En un artículo anterior te comentamos Cómo se utiliza Python y Django en Mejorando.la, hoy junto a Adán
Sanchez seguimos comentando algunas funciones y herramientas que se pueden ver en el repositorio Github
público del proyecto.
LA PARTE FUNDAMENTAL DE DJANGO: EL PATRON MTV
En el articulo anterior Adán ya nos había comentado sobre lo indispensable que es el “ceñirse fielmente” al
patrón MTV (o MVC) al utilizar Django. Ahora hablemos de los templates, parte importante de este patrón.
Templates
Nos dice Adán que una de las partes más fuertes de Django es su motor de plantillas, el mismo permite abstraer
completamente la presentación de las demás partes de la aplicación, esto gracias a etiquetas y filtros:
Filtros
“En las plantillas de Django hacemos uso de la rica cantidad de filtros que pone a nuestra disposición. Desde
convertir a mayúsculas, eliminar etiquetas html y agregar saltos de línea.” comentó Adán y agregó que gracias a
estos filtros es más fácil tener una clara separación entre datos, lógica y presentación.
Para dar formato a una fecha:
Ejemplo de filtros en plantillas
<p class=”fecha”>{{ video.fecha|date:"F • j"}}</p>
ALGUNAS BATERÍAS INCLUIDAS O MÓDULOS DJANGO
Ya habíamos mencionado que Django es el framework con baterías incluidas, aqui te comentamos algunas de
ellas:
Localización
Nos comenta Adán sobre GeoIP, una librería de localización basada en C: “La integración que trae Django con
esta librería da mucho poder a la hora de crear sitios internacionales.” Agrega que “esta librería básicamente es
una base de datos gigantesca, optimizada para ser de rápido acceso, con los rangos de direcciones IP por
regiones, países, etc.”
Feed
A su vez el módulo de sindicalización se ciñe perfectamente a los patrones propuestos por Django, permite
tener todos los métodos y propiedades de los modelos, así como usar plantillas y filtros para la presentación.
Formularios
Django viene con una forma fácil de crear formularios a partir de modelos, con validación automática y
completamente personalizable desde la plantilla, Adán nos explica como:
Clase para formulario automático
Un formulario es tan sencillo como:
class VideoComentarioForm(ModelForm):
class Meta:
model
= VideoComentario
fields = ('autor', 'autor_email', 'autor_url', 'content')
Plantilla para personalizar formulario
<form>
<label for="author">nombre</label>{{ form.autor }}
{{ form.autor.errors }}
<label for="email">email</label>{{ form.autor_email }}
{{ form.content.errors }}
…
</form>
LO POCO QUE NO TIENE DJANGO, SE SOLUCIONA
Si es que Django no incluye algunos módulos, es extremadamente sencillo crear módulos adicionales que
encapsulen funcionalidad extra.
Imágenes
En Mejorando.la hay diferentes tipos de imágenes; estas imágenes distintas son generadas recortando y
optimizando desde el administrador, cuando se crea el video. Nos cuenta Adán que esta tarea fue muy fácil de
realizar gracias a lalibrería PIL.
“Esta librería está en C por lo que es muy veloz, en Python te vas a encontrar muchos módulos en C, sobre todo
aquellos que requieren más procesamiento.” agrega Adán.
Ejemplo de uso del módulo PIL
import Image
image = Image.open(path)
image = image.resize((newWidth, newHeight), Image.ANTIALIAS)
image.save(path)[ ejemplo ]
CONSEJOS PARA MEJORAR LA WEB CON DJANGO Y
PYTHON
Adán nos dio algunas consideraciones para tener en cuenta al llevar el sitio a producción:






Configurar un administrador, que recibirá alertas cuando algo salga mal.
No olvides crear tus páginas 400 y 500 (páginas de errores).
Profundizar en los múltiples parámetros de configuración que ofrece Django. Antes de llevar cualquier sitio
Django a producción lee la guia para que veas todo lo que puede “tweakear”.
Con una línea puedes activar el uso de memcached para cachear las páginas de Django. Es muy simple y puede
hacer una gran diferencia en el rendimiento de tu aplicación cuando tienes muchas visitas.
Hay muchas formas de llevar a producción un sitio Django. Mejorando.la utiliza una combinación de Nginx (para
archivos estáticos) y mod_wsgi, que es un módulo de apache, para el código python.
Con respecto al hosting, hostgator es una buena opción para iniciar probando Django, sin pagar mucho.
EL TRABAJO EN EQUIPO. NO TODO ES PROGRAMACIÓN
Además de las tecnologías que mencionamos, también se necesitaron herramientas que ayudaron al trabajo en
equipo.
1. BaseCamp, lo más importante es mantenerse comunicado con tu equipo.
2. Github, es una parte central para asegurar la fluidez del trabajo ya que acelera mucho el proceso de comunicación
al llevar un registro de “quién hizo qué”. En Mejorando.la se manejan dos ramas principales, hay dos sitios
montados uno en desarrollo y otro que es el produccion.
3. Feedback, al desarrollar un sitio es importante lo que opinan los usuarios, para ello Twitter ha brindado una
enorme cantidad de feedback muy valioso, “nunca ignoren a un usuario que les reporta un bug”.
4. Diferentes dispositivos, cada uno de los que forman parte del equipo cuentan con dispositivos diferentes, aún así
ayuda cualdo alguien manda un error con una captura de pantalla en X dispositivo.
5. Newrelic, es una herramienta muy completa para el monitoreo del sitio.
Es importante que estés consciente de que con Django las posibilidades son muchas, y conforme vayas
profundizando en el tema irás aprendiendo trucos.
Ya sabes cómo se utiliza python, django y otras tecnologías para desarrollar el sitio de Mejorando.la. El sitio es
Open Source y el código está disponible en Github.
Y si quieres profundizar más en el tema recuerda que puedes revisar la guía Django y la guía Python.
GUÍA PYTHON: CONOCIENDO A DETALLE
LAS SECUENCIAS
Este capítulo es complemento de la tercera parte de la guía: Listas, tuplas, diccionarios y estructuras de control,
si no lo has leído ¿Qué esperas?. En el tercer capítulo se muestra de forma concisa el funcionamiento de las
listas y las tuplas, me propongo agregar más detalle a lo mencionado allí. Veamos qué tienen en común, qué
tienen de diferente y cómo Python nos permite trabajar con cadenas como si fuesen listas de caracteres.
SECUENCIAS
Listas y tuplas en Python son objetos que contienen listas de datos a los que se accede mediante un índice, de
forma similar a los arreglos (vectores o matrices) de otros lenguajes. Pertenecen a un tipo de datos que Python
llama secuencias y que incluyen también a las cadenas de texto.
Antes de entrar en las diferencias entre los tipos de secuencias, veamos lo que tienen en común:






No hay un límite a la cantidad de elementos que pueden contener.
Pueden contener cualquier tipo de objeto, incluyendo otras secuencias. Por ejemplo, la forma de crear una matriz en
Python es crear una lista de listas.
No es necesario saber el tamaño (cantidad de elementos) que tendrá la secuencia al momento de crearla.
Soportan algunas funciones nativas de python:
o len(secuencia): devuelve la cantidad de elementos de la lista, tupla o cadena.
o max(secuencia): devuelve el mayor elemento.
o min(secuencia): devuelve el menor elemento.
Tienen dos métodos comunes:
o secuencia.index(‘x’): devuelve el índice de la primera ocurrencia de ‘x’ en la secuencia.
o secuencia.count(‘x’): devuelve el número de veces que aparece ‘x’ en la secuencia
Los elementos de la secuencia se acceden vía subíndices, que se indican entre corchetes [] después del nombre de la
variable que contiene a la secuencia:
o secuencia[0]: devuelve el primer elemento
o secuencia[2]: devuelve el tercer elemento (notar que se numeran desde 0 y no desde 1).
o secuencia[i]: devuelve el elemento i-1 de la secuencia.
o secuencia[-1]: devuelve el último elemento.
Si sabes algo de PHP o Javascript verás que las secuencias en Python son muy similares a los arreglos en estos
lenguajes. Veamos ahora con más detalles otras características importantes de las secuencias.
JOIN (UNIÓN)
Podemos tomar una secuencia (cuyos elementos sean cadenas) y transformarla en una cadena, usando la
función join(). Esta función coloca un separador entre dos elementos de la secuencia. Si guardamos nuestro
separador en una variable s, debemos escribir:
s.join(secuencia)
Por ejemplo, para separar con guiones una lista de números, hacemos:
>>> '-'.join(['1', '2', '3', '4', '5', '6'])
'1-2-3-4-5-6'
>>> ''.join(['1', '2', '3', '4', '5', '6']) # el separador puede ser la cadena vacía
'123456'
>>> ' es menor que '.join(['1', '2', '3', '4', '5', '6']) # o una palabra o frase
completa
'1 es menor que 2 es menor que 3 es menor que 4 es menor que 5 es menor que 6'
REBANADAS (SLICES)
Se puede obtener una parte de la secuencia original usando secuencia[x:y:z], con x, y, z enteros. Lo anterior
devuelve una nueva secuencia con las siguientes características:



Del mismo tipo que la original (una rebanada de una lista es una lista, una rebanada de una tupla es una tupla, y una
rebanada de una cadena es una cadena).
Conteniendo los elementos desde secuencia[x] hasta secuencia[y-1] (no incluye a secuencia[y]).
Salteándose z elementos cada vez.
Por ejemplo, para obtener los números impares en una lista del 1 al 10, podemos hacer lo siguiente:
>>> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10][0:10:2] # desde el elemento 0 al elemento 9, de 2
en 2.
[1, 3, 5, 7, 9]
Para obtener el segundo y tercer elemento de una tupla:
>>> (1, 2, 3, 4)[1:3]
(2, 3)
El tercer parámetro puede omitirse y eso quiere decir que no se deben saltear elementos (se asumirá z = 1). De
hecho, también pueden omitirse los demás: si se omite x se tomarán todos los elementos desde el primero, y si
se omite “y” se tomarán todos los elementos hasta el final. Por lo tanto:



sec[:4] devuelve los primeros 4 elementos (0, 1, 2, 3).
sec[4:] devuelve los elementos desde el 5º hasta el último.
sec[:] crea una secuencia con todos los elementos de la primera y es de hecho la forma usual de copiar una
secuencia.
ITERABLES
Tal y como se explicó en el capítulo 3, las listas y tuplas se pueden recorrer elemento a elemento con el
bucle for. La explicación de Eugenia Bahit es muy clara, por lo que solamente agregaré que el bucle for puede
recorrer cualquier tipo de secuencia, y como las cadenas son secuencias, se las puede recorrer letra a letra:
>>> saludo = "hola"
>>> for letra in saludo:
...
print "letra = ", letra
...
letra = h
letra = o
letra = l
letra = a
OPERADOR DE PERTENENCIA
Para probar si un elemento pertenece a una secuencia, usamos el operador in. Ejemplos:
>>> 'o' in 'hola' # ¿'o' pertenece a la cadena 'hola'?
True
>>> 1 in (1, 2) # ¿1 pertenece a la tupla (1, 2)?
True
>>> 'a' in ['b', 'c'] # ¿'a' pertenece a la lista ['b', 'c']?
False
>>> 'a' not in ['b', 'c'] # ¿'a' NO pertenece a la lista ['b', 'c']?
True
CONCATENACIÓN (SUMA)
La suma de dos secuencias a y b genera una nueva secuencia que contiene los elementos de ambas y en la que
los elementos de b aparecen luego de los elementos de a. Las secuencias deben ser del mismo tipo (no es
posible sumar cadenas y tuplas, o tuplas y listas, por ejemplo):
>>> 'ho' + 'la'
'hola'
>>> (1, 2) + (3, 4) + (5, 6)
(1, 2, 3, 4, 5, 6)
>>> (1, 2) + [3, 4]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate tuple (not "list") to tuple
MULTIPLICACIÓN
Python define la multiplicación de secuencias por un número entero n. El resultado de la operación es el mismo
que el de sumar la secuencia a sí misma n veces.
>>> 'a' * 5
'aaaaa'
>>> 'ab' * 2
'abab'
>>> (4, 5) * 3
(4, 5, 4, 5, 4, 5)
NOTA
Si sabes PHP, verás que lo siguiente es muy similar a cómo funciona la función list() de PHP.
DESEMPAQUETADO (UNPACKING)
Python permite asignar los elementos de una secuencia a diferentes variables. No se requiere un operador
especial, el desempaquetado es automático cuando se asigna (usando =) una secuencia a una lista de variables.
De forma algo más simple: del lado izquierdo del signo de igual se escribe una lista de variables separadas por
comas, y del lado derecho la secuencia que será desempaquetada.
La cantidad de variables del lado izquierdo debe ser la misma que la cantidad de elementos de la secuencia.
Como esto puede sonar complicado, mejor veamos algunos ejemplos:
>>> a, b, c, d = 'hola'
>>> print a
'h'
>>> print b
'o'
>>> print c
'l'
>>> print d
'a'
>>> a, b = [1, 2]
>>> print a
1
>>> print b
2
>>> a, b = 'hola' #ERROR: ¡4 valores en la secuencia y solamente 2 variables!
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: too many values to unpack
Esto es lo que todas las secuencias tienen en común. Veamos ahora las particularidades de cada una.
LISTAS
Las listas en Python son equivalentes a los arreglos en PHP o en Javascript. Para crear una lista, simplemente se
declaran sus elementos entre corchetes:
>>> mi_lista = [1, 'b', 'd', 23]
>>> mi_lista_vacia = [] # crea una lista sin elementos
Para agregar elementos a una lista, se utiliza el método append:
>>> mi_lista.append(10)
>>> print mi_lista
[1, 'b', 'd', 23, 10]
Para modificar un elemento particular de una lista, se asigna el nuevo valor al subíndice correspondiente:
>>> mi_lista[0] = 2
>>> print mi_lista
[2, 'b', 'd', 23, 10]
Incluso se pueden reemplazar trozos de una lista con otra, o con trozos de otra, usando la notación de rebanadas
(slices):
>>> mi_lista[0:2] = [3, 4] # reemplazar los dos primeros elementos de la lista con
los elementos de [3, 4]
>>> print mi_lista
[3, 4, 'd', 23, 10]
Es importante notar que lo anterior no es lo mismo que asignar una secuencia entera a un índice:
>>> mi_lista[0] = [3, 4] # el primer elemento de mi_lista es ahora la lista [3, 4]
>>> print mi_lista
[[3, 4], 4, 'd', 23, 10]
Las listas poseen algunos métodos propios: se puede eliminar un elemento conmi_lista.remove(), reordenar
la lista con mi_lista.sort(), o invertir el orden de sus elementos con mi_lista.reverse(). Para una lista
completa, lee la documentación sobre Mutable Sequence Types. Todas las secuencias pueden ser transformadas
a listas usando la función list().
TUPLAS
Las tuplas son como las listas, excepto que son inmutables (sus elementos no pueden ser modificados). Se
identifican fácilmente porque en vez de usar corchetes, se definen entre paréntesis. En lo demás, funcionan
igual a las listas y al resto de las secuencias.
>>> mi_tupla = (3, 4, 'b', 'd')
>>> mi_tupla[2] = 'hola' # ERROR: ¡la tupla no se puede modificar!
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
Python genera tuplas de forma automática cuando encuentra valores separados por comas en el código, aunque
no estén delimitados por paréntesis. Esta generación automática es lo contrario del desempaquetado, y se llama,
lógicamente, empaquetado (packing) de valores. Esto nos permite simular retorno de múltiples valores en las
funciones, o intercambiar los valores de dos variables sin crear una variable auxiliar:
var1, var2 = x, y
En este caso, Python crea una tupla (x, y) (empaquetado), e inmediatamente asigna el primer elemento a var1, y
el segundo a var2 (desempaquetado)
def mi_funcion():
# código de la función
...
return x, y
Aunque se especifiquen 2 variables, en realidad python crea la tupla (x, y) y eso es lo que devuelve. En el
código que llama a la función, podemos escribir
var1, var2 = mi_funcion()
Gracias al desempaquetado, var1 obtiene el valor de “x” y var2 el de “y”. Todas las secuencias pueden ser
transformadas a tuplas usando la función tuple().
CADENAS
Las cadenas son secuencias cuyos elementos son los caracteres individuales. Por la importancia que tiene el
texto en casi cualquier programa y dado que las cadenas tienen muchas operaciones específicas que no están
definidas para las demás secuencias.
En el capítulo de la próxima semana, detallaremos las características únicas de las cadenas de texto en Python.
VENTAJAS PARA LOS QUE SE PONEN LA
CAMISETA DE PYTHON
Así como apoyamos la educación en línea y diversos eventos “de la web” nos “ponemos la camiseta” de
algunos lenguajes y frameworks que creemos que son los que valen la pena aprender y desarrollar.
Hoy quiero hablarte de “por qué nos ponemos la camiseta de Python y Django“, contandote las ventajas y
desventajas del lenguaje y del framework (respectivamente) con la ayuda de tres programadores: Arturo
Jamaica, Adán Sanchez y Ricardo Azpeitia.
DIFERENTES MOTIVOS, UN MISMO LENGUAJE.
Ricardo Azpeitia es de Monterrey, Mexico y es un destacado usuario de forosdelweb con varios tutoriales sobre
python en su haber y siempre dispuesto a responder dudas sobre dicho lenguaje. Comenzó a aprender
programación con Visual Basic y fue evolucionando a través de los años, luego, desde que está en la
universidad comenzó a aprender python por su cuenta.
Arturo Jamaica es de Querétaro, México , tiene 22 años, estudia ingeniería en sistemas y es el creador de
brounie.com. Comenzó con python porque un compañero se lo recomendó y aunque en principio lo vió como
“un juego” actualmente realizó varios cursos de python con Mejorandola y estuvo en algunos
programas enseñando python en 20 minutos e inclusive participó de Mejorando.la Conferencia.
Adan Sanchez o mejor conocido como dual3nigma, es de Coatepec. México y es uno de los programadores del
proyecto Mejorando.la. Comenzó gracias a Blender (un programa de diseño en 3D) ya que tiene como intérprete
del motor de juegos a python y así como desde hace 5 años que trabaja con dicho lenguaje.
VAYAMOS AL GRANO, VENTAJAS Y DESVENTAJAS DE
PYTHON
Ventajas



Simplificado y rápido: Nos dice Adán que lo bueno de python es que simplifica mucho la programación “hace que
te ciñas a un modo de lenguaje de programación, python te propone un patrón”. Por su parte Ricardo señala que es
un gran lenguaje para scripting, “si quieres algo rápido (en el sentido del performance del lenguaje), con unas
cuantas líneas ya está”.
Elegante y flexible: Para Ricardo el lenguaje te da muchas herramientas “si quiero listas de varios datos, no hace
falta que declares cada cosa” y agrega que al ser tan flexible no te preocupas tanto por los detalles.
Programación sana y productiva: Según Arturo programar en python se convierte en un estilo muy sano de
programar: “es sencillo de aprender, direccionado a las reglas perfectas, te haces como dependiente de mejorar,
cumplir las reglas, el uso de las lineas, de variables”. Adán afirma que esun lenguaje que fue hecho con
productividad en mente “python me hace ser mas productivo, me permite entregar en los tiempos que me piden”.




Ordenado y limpio: Este es un punto en el cual los tres coinciden. Dice Arturo que es el orden que mantiene python
es de lo que más le gusta “es muy leible, cualquier otro programador lo puede leer y trabajar sobre el”. A su vez
Adán destaca que los módulos están bien organizados, a diferencia de otros lenguajes.
Portable: Tanto Arturo como Adán concuerdan en que es un lenguaje muy portable (ya sea en mac, linux o
windows) en comparación con otros lenguajes.
Baterías incluidas: “Las librerías que más necesitas ya están dentro del código” menciona Arturo. Así también
Adán comentó en detalle cuáles son estas baterías incluidas al contarnos “cómo se utiliza python en mejorando.la“
Comunidad: Algo muy importante para el desarrollo de un lenguaje es la comunidad, según Arturo “la misma
comunidad de python cuida el lenguaje y casi todas las actualizaciones se hacen de manera democrática.”
Desventajas
Arturo mencionó que la “curva de aprendizaje cuando ya estás en la parte web no es tan sencilla”. Por su parte
Adán comentó sobre el hecho de que “la mayoría de los servidores no tienen python” y si lo tienen la
configuración es un poco dificil.
Ricardo dijo que no le gustan algunas librerías que trae por defecto, por ejemplo las que trae para trabajar con
http y algunas que están hechas por terceras personas.
HERRAMIENTAS PARA PONERSE LA CAMISETA DE
PYTHON
Hay personas que no quieren “casarse” con ningún lenguaje, sin embargo Arturo nos dice que él se pone la
camiseta de python y hace todo lo posible para difundir el aprendizaje del lenguaje. Así también nosotros, por
ello si quieres ponerte la camiseta de python puedes seguir la guía Python (que está teniendo nuevos capitulos
imperdibles) y la guia Django.
Además puedes ver cómo se utiliza python y conocer el repositorio de mejorando.la.
GUÍA PYTHON: CADENAS DE TEXTO
En el capítulo anterior vimos en detalle el manejo de secuencias en Python y que las cadenas son un tipo
especial de secuencia. Puede parecer extraño considerar a las cadenas de la misma forma que las tuplas o listas,
sobretodo porque se definen de una forma tan diferente: no se separan sus elementos, por ejemplo, ni utilizan
paréntesis o corchetes. Sin embargo tiene mucho sentido hacerlo, ¿qué otra cosa es el texto sino
una secuencia de caracteres?
DEFINIENDO CADENAS
En Python tenemos varias formas ligeramente diferentes de definir cadenas. La forma más común es escribirlas
entre comillas dobles (“):
cadena = "hola"
También pueden utilizarse comillas simples (‘), no hay diferencia entre las cadenas delimitadas con ” o ‘.
Por razones obvias, no podemos incluir en la cadena una comilla del mismo tipo que la que se utiliza para
definirla, Python no sabrá dónde termina realmente:
>>> mi_cadena = "hola "amigo" "
File "", line 1
mi_cadena = "hola "mundo" "
^
SyntaxError: invalid syntax
Una forma posible de evitar esto es delimitar la cadena con comillas simples si sabemos que contendrá comillas
dobles y delimitarla con comillas dobles si sabemos que contendrá comillas simples.
Tampoco podemos incluir saltos de línea en la cadena:
>>> mi_cadena = "hola
File "", line 1
mi_cadena = "hola
^
SyntaxError: EOL while scanning string literal
Para solucionar esto, tenemos la opción de utilizar secuencias de escape. Las secuencias de escape permiten
introducir caracteres especiales, escapándolos(forzándolos a ser caracteres sin significado especial) con una
contrabarra (\) delante. La secuencia de escape para un salto de línea es “\n”:
>>> print "hola \n \" mundo \" " # los espacios antes y después de \n no son
necesarios, se agregan por claridad
hola
" mundo "
Para solucionar el problema de los saltos de línea o las comillas, podemos utilizar también una tercera vía: las
cadenas en Python pueden delimitarse con bloques de tres comillas dobles (“””) o tres apóstrofes (”’). Saltos de
línea y comillas individuales están permitidos en este tipo de cadenas.
>>> print """hola
"mundo" """
hola
"mundo"
Además de todo esto, hay dos modificadores (tres en Python 3) que cambian la forma en la que la cadena es
interpretada, anteponiéndolos a la cadena misma:


‘r’ fuerza a que las secuencias de escape no sean interpretadas: r'hola\nmundo' no convierte “\n” a un salto de
línea.
‘u’ fuerza a que la cadena sea considerada una cadena Unicode, esto habilita algunas secuencias de escape extras y
cambia la codificación que será utilizada para la cadena.
Podemos encontrar más detalles sobre los modificadores, las cadenas de escape, y en general sobre cómo se
definen las cadenas en Python, en la documentación sobre Lexical Analysis – String Literals.
INMUTABLES
En Python, así como en Java y otros lenguajes, las cadenas son inmutables (sus elementos no se pueden
modificar). Si se requieren modificaciones, se debe construir una cadena nueva (muchas veces esto lo hace
automáticamente el intérprete de Python). Ejemplo:
>>> cadena = "Hola"
>>> cadena[2] = "a"
Traceback (most recent call last):
File "", line 1, in
TypeError: 'str' object does not support item assignment
>>> print cadena[:2] + 'a' + cadena[3] # se crea una cadena nueva a partir de la
original
Hoaa
MÉTODOS DE CADENAS
Además de la sintaxis de subíndices y rebanadas (discutidas en el capítulo anterior sobre Secuencias), las
cadenas en Python tienen algunos métodos para operaciones comunes (búsqueda y reemplazo, por ejemplo).
BÚSQUEDA
Para buscar una subcadena (o un caracter) en una cadena, Python nos ofrece varias alternativas. Si solamente
necesitamos saber si una cadena contiene cierto caracter o cierta subcadena, usamos el operador in:
>>> if 'la' in 'hola':
...
print '¡Está!'
...
¡Está!
Si necesitamos saber además la posición en la que se encuentra la subcadena, usamos los
métodos index() o find(). La diferencia entre ambos es que, si no se encuentra la
subcadena, find() devuelve -1, mientras que index() lanza una excepción de tipo ValueError. Ejemplos:
>>> cadena = "hola"
>>> cadena.find('ha') # 'ha' no está en 'hola', find() retorna -1
-1
>>> cadena.index('ha')
# index() lanza una excepción
Traceback (most recent call last):
File "", line 1, in
ValueError: substring not found
>>> cadena.index('a')
3
>>> cadena.find('a') # si la cadena está, tanto index() como find() funcionan igual
3
>>> if cadena.find('ha') == -1:
...
print 'No encontrada'
...
No encontrada
>>> try:
...
cadena.index('ha')
... except ValueError:
...
print 'No encontrada'
...
No encontrada
Un uso común de ambos métodos es obtener toda la cadena desde el principio hasta la primer ocurrencia de
cierta subcadena:
>>> # guardamos en pos_la la posición de 'la' en 'hola' (tercer caracter)
>>> pos_la = 'hola'.find('la')
>>> # pedimos a Python imprimir la cadena 'hola' desde el principio hasta el tercer
caracter (sin incluirlo)
>>> print 'hola'[:pos_la]
ho
Finalmente, tanto index como find aceptan parámetros que restringen la búsqueda a cierto tramo de la cadena:
en vez de buscar desde el principio y hasta el final. El segundo argumento del método indica desde qué posición
comenzar a buscar y el tercero indica en qué posición terminar la búsqueda.
Para ejemplificar, busquemos todas las ocurrencias de la letra e en una cadena cualquiera (nota: hay mejores
maneras de hacer esto, ¡lo siguiente es solamente un ejemplo!)
cadena = '¡La mejor Guía de Python está en Maestros del Web!'
lista = []
pos_inicial = -1
try:
while True:
# cada vez buscamos desde un caracter más adelante de
# la última ocurrencia encontrada
pos_inicial = cadena.index('e', pos_inicial+1)
lista.append(pos_inicial)
except ValueError: # cuando ya no se encuentre la letra
print 'Posiciones de la letra "e" en la cadena:', lista
Posiciones de la letra "e" en la cadena: [6, 18, 27, 33, 38, 46, 50]
REEMPLAZO DE TEXTO
Otra operación común es reemplazar una parte de una cadena por otra. En Python esto lo hacemos con el
método replace:
>>> cadena = "Esto será reemplazado: hola"
>>> print cadena.replace('hola', 'mundo')
Esto será reemplazado: mundo
Notar que, como las cadenas son inmutables, el método replace devuelve una nueva cadena con el texto
reemplazado, la cadena original (en el ejemplo,cadena) queda intacta.
DIVISIÓN EN TROZOS
Supongamos que tenemos una cadena que contiene una fecha, en formato día/mes/año. Podemos obtener
fácilmente cada trozo de la cadena (cada dato de la fecha) utilizando el método split. Este método divide a la
cadena en trozos, cortando cada trozo en cada ocurrencia de un separador, que se pasa como argumento.
>>> fecha = '17/05/2012'
>>> datos = fecha.split('/') # separamos la cadena por /
>>> print datos
['17', '05', '2012']
# la lista contiene los trozos, sin el separador
>>> print 'día:', datos[0], 'mes:', datos[1], 'año:', datos[2]
día: 17 mes: 05 año: 2012
Si no le damos a split un separador, la cadena será separada por espacios. Esto puede servir para obtener todas
las palabras de una oración:
>>> "¡La mejor Guía de Python está en Maestros del Web!".split()
['¡La', 'mejor', 'Guía', 'de', 'Python', 'está', 'en', 'Maestros', 'del', 'Web!']
La operación inversa (convertir una lista a cadena), se hace con el método join, que vimos en el capítulo sobre
Secuencias. Una advertencia a quienes programen en otros lenguajes: la operación join se define usualmente
como una operación de la lista, que toma una cadena como argumento y la usa como delimitador. En Python,
sin embargo, join es una operación de la cadena y toma una lista como argumento.
Como ejemplo, volvamos a unir la cadena de fecha, con diferentes caracteres delimitadores:
>>> fecha = "17/05/2012"
>>> lista = fecha.split("/")
>>> print lista
['17', '05', '2012']
>>> print "-".join(lista)
17-05-2012
>>> print " ".join(lista)
17 05 2012
>>> print ", ".join(lista)
17, 05, 2012
Notar que si bien es un ejemplo claro, sería más sencillo y eficiente hacer la sustitución sobre la cadena original
con replace('/', '-').
CONCLUSIÓN
Hemos visto cómo realizar en Python algunas de las operaciones más comunes sobre cadenas: definición,
búsqueda, reemplazo de subcadenas y separación en trozos.
Las cadenas tienen otros métodos para operaciones varias, como por ejemplo buscar desde la derecha en vez de
desde la izquierda, o convertir la cadena entera o parte de ella a mayúsculas o minúsculas. La lista completa de
métodos está, como siempre, en la documentación: The string module y String methods.
GUÍA PYTHON: EXPRESIONES REGULARES
Cuando manejamos texto, sin duda una de las operaciones más comunes es la búsqueda de una subcadena, ya
sea para obtener su posición en el texto o simplemente para comprobar si está presente. Si la cadena que
buscamos es fija, son suficientes los métodos como find(), index() o similares, pero éstos no ayudan si lo que se
busca es una subcadena con cierta forma.
Al buscar direcciones de correo electrónico, números de teléfono, validar campos de entrada, o encontrar por
ejemplo una letra mayúscula seguida de dos minúsculas y de 5 dígitos entre 1 y 3, es necesario recurrir a
las Expresiones Regulares, también conocidas como Patrones.
PATRONES
Las expresiones regulares son un lenguaje potente de descripción de texto, y no creo que exista un lenguaje
moderno que no permita usarlas. Las reglas con las que se forman son bastante simples, pero requiere práctica
aprender a combinarlas correctamente.
Con expresiones regulares podemos buscar una subcadena al principio o al final del texto, si queremos que se
repita cierta cantidad de veces, si queremos que algo NO aparezca, o si debe aparecer una subcadena entre
varias posibles. Permite además capturar aquellos trozos del texto que coincidan con la expresión, para
guardarlos en una variable o reemplazarlos por una cadena predeterminada (o incluso una cadena formada por
los mismos trozos capturados). Veremos algunos aspectos básicos de las expresiones regulares, sin entrar en
detalles.
METACARACTERES
Se conoce como metacaracteres a aquellos caracteres que, dependiendo del contexto, tienen un significado
especial para las expresiones regulares, y que por lo tanto debemos escapar (colocándoles una contrabarra \
delante) si queremos buscarlos explícitamente. A continuación veremos los más importantes:

Anclas: Indican que lo que queremos encontrar se encuentra al principio o al final de la cadena. Combinándolas,
podemos buscar algo que represente a la cadena entera:
^patron: coincide con cualquier cadena que comience con patron.
patron$: coincide con cualquier cadena que termine con patron.
^patron$: coincide con la cadena exacta patron.

Clases de caracteres: Se utilizan cuando se quiere buscar un caracter dentro de varias posibles opciones. Una clase
se delimita entre corchetes (paréntesis rectos) y lista posibles opciones para el caracter que representa:
[abc]: coincide con a, b, o c
[387ab]: coincide con 3, 8, a o b
niñ[oa]s: coincide con niños o niñas.
Para evitar errores, en caso de que queramos crear una clase de caracteres que contenga un corchete,
debemos escribir una barra \ delante, para que el motor de expresiones regulares lo considere un caracter
normal: la clase[ab\[] coincide con a, b y [.
RANGOS
Si queremos encontrar un número, podemos usar una clase como [0123456789], o podemos utilizar un rango.
Un rango es una clase de caracteres abreviada que se crea escribiendo el primer caracter del rango, un guión, y
el último caracter del rango. Múltiples rangos pueden definirse en la misma clase de caracteres.



[a-c]: equivale a [abc]
[0-9]: equivale a [0123456789]
[a-d5-8]: equivale a [abcd578]
Es importante notar que si se quiere buscar un guión, debe colocarse al principio o al final de la clase
(inmediatamente después del corchete izquierdo o inmediatamente antes del corchete derecho) o escaparse. Si
no se hace de esta forma, el motor de expresiones regulares intentará crear un rango y la expresión no
funcionará como debe (o dará un error). Si queremos, por ejemplo, crear una clase que coincida con los
caracteres a, 4 y -, debemos escribirla como:



[a4-]
[-a4]
[a\-4]
RANGO NEGADO
Así como podemos listar los caracteres posibles en cierta posición de la cadena, también podemos listar
caracteres que NO deben aparecer. Para lograrlo, debemosnegar la clase, colocando un circunflejo
inmediatamente después del corchete izquierdo:

[^abc]: coincide con cualquier caracter distinto a a, b y c
CLASES PREDEFINIDAS
Hay algunas clases que se usan frecuentemente y por eso existen formas abreviadas para ellas. En Python (así
como en otros lenguajes) se soportan las clases predefinidas de Perl y de POSIX (si no sabes lo que eso quiere
decir, quizás quieras leer en Wikipedia su signficado). Algunas de estas clases son:



\d (POSIX [[:digit:]]): equivale a [0-9]
\s (POSIX [[:space:]]): caracteres de espacio en blanco (espacio, tabulador, nueva línea, etc)
\w (POSIX [[:word:]]): letras minúsculas, mayúsculas, números e infraguión (_)
Además de las listadas arriba (y el resto, no listadas) existe una clase de caracteres que coincide con cualquier
caracter (sea letra, número, o un caracter especial). Esta clase es el punto:

"." : coincide con cualquier caracter.
CUANTIFICADORES
Son caracteres que multiplican el patrón que les precede. Mientras que con las clases de caracteres podemos
buscar un dígito, o una letra, con los cuantificadores podemos buscar cero o más letras, al menos 7 dígitos,
o entre tres y cinco letras mayúsculas.
Los cuantificadores son:





?: coincide con cero o una ocurrencia del patrón (dicho de otra forma: hace que el patrón sea opcional)
+: coincide con una o más ocurrencias del patrón
*: coincide con cero o más ocurrencias del patrón.
{x}: coincide con exactamente x ocurrencias del patrón
{x, y}: coincide con al menos x y no más de y ocurrencias. Si se omite x, el mínimo es cero, y si se omite y, no hay
máximo. Esto permite especificar a los otros como casos particulares: ? es {0,1}, + es {1,} y * es {,} o{0,}.
Ejemplos:
.* : cualquier cadena, de cualquier largo (incluyendo una cadena vacía)
[a-z]{3,6}: entre 3 y 6 letras minúsculas
\d{4,}: al menos 4 dígitos
.*hola!?: una cadena cualquiera, seguida de hola, y terminando (o no)
con un !
OTROS METACARACTERES
Existen otros metacaracteres en el lenguaje de las expresiones regulares:

?: Además de servir como cuantificador, puede modificar el comportamiento de otro. De forma predeterminada, un

cuantificador coincide con la mayorcadena posible; cuando se le coloca un ?, se indica que se debe coincidir con
la menor cadena posible. Esto es: dada la cadena bbbbb, b+ coincide con la cadena entera, mientras
que b+? coincide solamente con b (la menor cadena que cumple el patrón).
(): agrupan patrones. Sirven para que aquel trozo de la cadena que coincida con el patrón sea capturado (veremos
luego cómo usar el valor capturado), o para delimitar el alcance de un cuantificador. Ejemplo: ab+coincide
con ab, abb, abbbbb, ..., mientras que (ab)+ coincide con ab,abab, abab...
| : permite definir opciones para el patrón: perro|gato coincide con perro y con gato.

MÓDULO RE
Para utilizar Expresiones Regulares, Python provee el módulo re. Importando este módulo podemos crear
objetos de tipo patrón y generar objetos tipo matcher, que son los que contienen la información de la
coincidencia del patrón en la cadena.
CREANDO UN PATRÓN
Para crear un objeto patrón, importamos el módulo re y utilizamos la funcióncompile:
import os
patron = re.compile('a[3-5]+') # coincide con una letra, seguida de al menos 1 dígito
entre 3 y 5
A partir de ahora, podemos usar el objeto patron para comparar cadenas con la expresión regular.
BUSCAR EL PATRÓN EN LA CADENA
Para buscar un patrón en una cadena, Python provee los métodos search y match. La diferencia entre ambos es
que, mientras search busca en la cadena alguna ocurrencia del patrón, match devuelve None si la ocurrencia no
se da al principio de la cadena:
>>> cadena = 'a44453'
>>> patron.match(cadena)
<_sre.SRE_Match object at 0x02303BF0>
>>> patron.search(cadena)
<_sre.SRE_Match object at 0x02303C28>
>>> cadena = 'ba3455' # la coincidencia no está al principio!
>>> patron.search(cadena)
<_sre.SRE_Match object at 0x02303BF0>
>>> print patron.match(cadena)
None
Si sabemos que obtendremos más de una coincidencia, podemos usar el métodofindall, que recorre la cadena y
devuelve una lista de coincidencias:
>>> patron.findall('a455 a333b435')
['a455', 'a333']
O el método finditer, que devuelve un iterador que podemos usar en el bucle for:
>>> for m in patron.finditer('a455 a333b435'):
# cada m es un objeto tipo matcher
...
print m.groups()
...
('a', '455')
('a', '333')
('b', '435')
OBJETOS MATCHER
Más arriba se mencionó el uso del los paréntesis en un patrón. Cuando se obtiene una coincidencia del patrón en
una cadena, cada grupo delimitado por paréntesis captura el texto que haya coincidido con él. Estos grupos son
accesibles a través de un objeto tipo matcher devuelto por search o match. Los grupos se numeran de izquierda
a derecha según su orden de aparición en el patrón, y podemos usar este número para acceder al contenido del
grupo con el método group del objetomatcher.
De forma alternativa, podemos usar el método groups que devuelve una lista de grupos.
>>> patron = re.compile('([ab])([3-5]+)')
# ahora la letra se capturará en el grupo
1, y los números en el 2
>>> matcher = patron.search('a455 a333b435')
>>> matcher.group(0)
# el grupo 0 es el trozo de cadena que coincidió con el patrón
completo
'a455'
>>> matcher.group(1)
'a'
>>> matcher.group(2)
'455'
>>> matcher.groups()
# groups() no incluye el grupo 0
('a', '455')
Los objetos matcher guardan más información sobre la coincidencia, por ejemplo la posición de la cadena en la
que se produjo (en este caso, al principio de la cadena):
>>> matcher.pos
0
También permiten sustituir los grupos capturados en una cadena cualquiera, mediante el uso de referencias de la
forma \g<x>, donde x es el número de grupo:
>>> print matcher.expand('La cadena que coincidió fue \g<0>, el grupo 1 es \g<1> y el
grupo 2 es \g<2>')
La cadena que coincidió fue a455, el grupo 1 es a y el grupo 2 es 455
Vale notar que, si bien findall no devuelve objetos tipo matcher, sí proporciona los grupos de forma similar,
como una lista de tuplas:
>>> patron.findall('a455 a333b435')
[('a', '455'), ('a', '333'), ('b', '435')]
REEMPLAZO DE CADENAS
Similar a la combinación search + expand, existe el método sub, cuya función es encontrar todas las
coincidencias de un patrón y sustituirlas por una cadena. El método recibe dos parámetros: el primero es la
cadena con la que se sustituirá el patrón y el segundo es la cadena sobre la que queremos aplicar la sustitución.
Se pueden utilizar referencias de la misma forma que antes:
>>> patron.sub("X", 'a455 a333b435')
# sustituye todas las ocurrencias por X
'X XX'
>>> patron.sub("LETRA(\g<1>), NUMERO(\g<2>)", 'a455 a333b435')
# El reemplazo
depende de lo que se capture
'LETRA(a), NUMERO(455) LETRA(a), NUMERO(333)LETRA(b), NUMERO(435)'
GRUPOS CON NOMBRE
De la misma forma en la que podemos usar grupos numerados, también podemos usar grupos con nombre. Esto
hace más cómodo el manejo de patrones complejos, ya que siempre es más natural manejar un nombre que un
número. Además, si solamente usamos números de grupo, podemos tener errores si luego modificamos el
patrón para agregar algún grupo: al agregarlo bien podríamos estar cambiando el índice de otro posterior.
Los nombres de grupo se definen agregando ?P<nombre_de_grupo> al paréntesis de apertura del grupo.
>>> patron = re.compile('(?P<letra>[ab])(?P<numero>[3-5]+)')
# defino dos grupos con
nombre 'letra' y 'numero'
>>> matcher = patron.search('a455 a333b435')
>>> matcher.groups()
('a', '455')
>>> matcher.group(1)
'a'
# busco en la misma cadena de antes
# groups y group(n) funcionan igual
>>> matcher.group('letra')
# pero además ahora puedo acceder por nombre
'a'
>>> matcher.group('numero')
'455'
>>> matcher.expand('La letra es \g<letra>')
# las referencias se usan con el nombre
en vez de con el número
'La letra es a'
Otra ventaja de utilizar nombres de grupo, es que podemos usar el métodogroupdict para obtener un diccionario
de pares nombre-contenido de cada grupo:
>>> matcher.groupdict()
{'letra': 'a', 'numero': '455'}
MODIFICADORES PARA EL PATRÓN
Existen varios modificadores que podemos pasar al método compile para modificar el comportamiento del
patrón. Los más usados son:


re.I o re.IGNORECASE: hace que el patrón no distinga entre minúsculas y mayúsculas.
re.M o re.MULTILINE: modifica el comportamiento de ^ y $ para que coincidan con el comienzo y final de cada

línea de la cadena, en vez de coincidir con el comienzo y final de la cadena entera
re.S o re.DOTALL: hace que el punto (.) coincida además con un salto de línea (sin este modificador, el punto
coincide con cualquier caracter excepto un salto de línea)
Cada modificador se usa como segundo parámetro de la función, podemos unir los efectos de más de un
modificador separándolos con |. Por ejemplo:
>>> patron = re.compile('el patron', re.I | re.MULTILINE)
La lista completa está en 7.2. re — Regular expression operations.
La próxima semana trabajaremos con Diccionarios, funciones y archivos en Python.
GUÍA PYTHON: MANEJANDO ARCHIVOS,
DICCIONARIOS Y FUNCIONES
DICCIONARIOS
Conocidos en otros lenguajes como Hashes (Perl), arreglos asociativos (PHP) ohashmaps (Java), los
diccionarios en Python son contenedores de pares clave-valor. A simple vista, son simplemente arreglos o listas
cuyos índices son cadenas en vez de ser números enteros. Internamente la diferencia es mucho mayor, ya que se
implementan como tablas hash en vez de listas. Pero no vale la pena entrar aquí en esos detalles, cuando
podemos encontrar descripciones precisas en Wikipedia: Lista, Tabla Hash.
Los diccionarios no preservan el orden de los elementos: éste depende de las claves que se usen, del orden en el
que se ingresan los datos y de otros factores. Además, no se puede iterar directamente sobre los elementos,
aunque Python provee funciones para hacerlo (y también para iterar sobre las claves).
Para crear un diccionario, escribimos los pares clave-valor entre llaves, separando cada par por una coma:
>>> mi_diccionario = { 'nombre': 'Juan', 'apellido': 'Perez', 'pais':
'Uruguay' }
>>> print mi_diccionario['nombre']
Juan
Un diccionario puede ser expandido simplemente asignando un valor a un nuevo índice. También podemos
eliminar un valor del diccionario usando del:
>>> mi_diccionario['edad'] = 32
>>> del mi_diccionario['pais']
>>> print mi_diccionario
{'edad': 32, 'nombre': 'Juan', 'apellido': 'Perez'} # notar que se
agregó la clave 'edad', se eliminó la clave 'país', y no se preservó el
orden
Si intentamos acceder a una clave no definida, Python lanza la excepción KeyError. Para evitarla, podemos
comprobar fácilmente si la clave está en el diccionario usando el operador in:
>>> print mi_diccionario['una_clave_que_no_existe']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'una_clave_que_no_existe'
>>> if 'edad' in mi_diccionario:
...
print mi_diccionario['edad']
...
32
Otra forma de evitar el error es utilizar el método get(). Este método recibe dos parámetros: la clave cuyo valor
se quiere obtener y el valor a retornar si la clave no está definida. Si no se especifica el segundo
parámetro, get devuelve None:
>>> print mi_diccionario.get('edad')
32
>>> print mi_diccionario.get('una_clave_que_no_existe')
None>>> print mi_diccionario.get('una_clave_que_no_existe',
'valor predeterminado')
valor predeterminado
Al igual que las secuencias, los diccionarios también pueden ser recorridos con un bucle for. La diferencia es
que en vez de iterar en orden sobre los elementos, el bucle for itera sobre las claves sin un orden
preestablecido:
>>> for clave in mi_diccionario:
...
print clave, ": ", mi_diccionario[clave]
...
edad :
32
nombre :
Juan
apellido :
Perez
Usando el método items(), obtenemos una lista de tuplas (clave, valor), que podemos usar en el for:
>>> for (clave, valor) in mi_diccionario.items():
...
print clave, ": ", valor
...
edad :
32
nombre :
Juanapellido :
Perez
Los diccionarios soportan otras operaciones: el método copy() devuelve una copia, y el método update() permite
agregar a un diccionario las claves y valores de otro:
>>> dic = mi_diccionario.copy()
>>> print dic
{'edad': 32, 'nombre': 'Juan', 'apellido': 'Perez'}
>>> dic.update({'pais': 'Uruguay', 'idioma': 'Español' })
>>> print dic
{'edad': 32, 'nombre': 'Juan', 'idioma': 'Español', 'apellido': 'Perez',
'pais': 'Uruguay'}
FUNCIONES
Las funciones en Python se declaran con la palabra clave def, seguida del nombre de la función y de sus
argumentos. Como todo bloque en Python, la declaración termina con dos puntos (“:”) y el código siguiente
debe estar sangrado a un nivel mayor:
def mifuncion(parametro1, parametro2):
código de la función
Una vez definida, la función puede ser llamada desde cualquier parte de nuestro archivo Python, simplemente
con su nombre. Además de recibir valores por vía de sus parámetros, la función puede acceder a variables
definidas fuera de ella y en el mismo archivo (módulo), usando la sentencia global.
Para devolver un valor, se utiliza la sentencia return. Este valor se puede obtener por asignación. Veamos un
ejemplo:
variable_externa = 'HOLA'
def mi_funcion(param1, param2, ...):
# código de la función
global variable_externa
return variable_externa
mifuncion(....) # ejecuta el código de la función
mi_variable1 = mi_funcion(var1, var2, ...)
# mi_variable = 'HOLA'
Vale notar que la función puede devolver solamente un valor (objeto), sin embargo, se puede simular la
devolución de valores múltiples gracias al empaquetado: valores separados por coma luego de la
sentencia return se convierten a una tupla y lo que devuelve la función es esa tupla. Luego podemos recibir los
valores devueltos como una tupla o desempaquetarlos:
>>> def mi_f():
...
return 'a', 'b'
...
>>> var1, var2 = mi_f()
# var1 = 'a', var2 = 'b'
>>> t_var = mi_f()
# t_var: tupla formada por 'a' y 'b'
>>> t_var
('a', 'b')
Las funciones en Python pueden además asignarse y pasarse como parámetro a otras funciones.
MANEJANDO ARCHIVOS
En Python, así como en cualquier otro lenguaje, los archivos se manipulan en tres pasos: primero se abren,
luego se opera sobre ellos y por último se cierran.
APERTURA
Para abrir un archivo debemos usar la función open(), que recibe como parámetros el nombre del archivo y el
modo en el que se debe abrir. De forma predeterminada (es decir, si se omite el segundo parámetro), el archivo
se abre como sólo lectura.
Es importante tener en cuenta que todas las operaciones están limitadas a la forma en la que se abra el archivo:
no se puede leer de un archivo abierto solamente para escritura, ni escribir en un archivo abierto como solo
lectura.
MODOS



r: Sólo lectura. No se podrá escribir en el archivo.
w: Sólo escritura. Trunca el archivo al momento de abrirlo.
a: Sólo escritura. Escribe al final del archivo.
En cualquiera de los modos, si el archivo no existe, es creado. Opcionalmente se puede añadir + al modo para
que se abra en modo lectura y escritura a la vez; aunque esto no suele ser necesario y requiere cuidado para que
funcione correctamente.
Otro modificador posible es b, que sirve para trabajar con archivos binarios. Esto es necesario en Windows para
manejar correctamente archivos de imágenes, o música (toda clase de archivos que no sean texto simple),
porque el mismo SO hace diferencia entre archivos binarios y de texto. Esto no sucede en sistemas tipo UNIX
(como Mac OS, o Linux), y por tanto en estos sistemas el modificador b no hace ninguna diferencia.
LECTURA
Una vez abierto el archivo, podemos leer el contenido hacia una cadena conread(), leer una línea con readline(),
u obtener una lista conteniendo las líneas del archivo con readlines(). Los tres métodos aceptan un parámetro
entero opcional que define el número máximo de bytes a leer del archivo. Si este parámetro es negativo o
simplemente se omite, read y readlines leerán todo el archivo yreadline una línea completa sin importar su
largo.
Otra forma de leer el archivo es leer línea por línea en un bucle for, ya que el objeto archivo es iterable.
Para ejemplificar lo mencionado hasta ahora, supongamos que tenemos un archivoprueba.txt con el siguiente
contenido:
Esto es
una prueba
de lectura!
tenemos varias formas de leerlo:
>>> archivo = open('prueba.txt', 'r') # sólo lectura. Con modificadores, podría usar
'r+', 'rb', o 'rb+'
>>> print archivo.read()
# leer todo
Esto es
una prueba
de lectura!
>>> print archivo.readline()
# leer 1 línea.
Esto es
>>> print archivo.readlines()
# leer todas las líneas como una lista.
['Esto es\n', 'una prueba\n', 'de lectura!']
# nótese que siempre se
incluyen los saltos de
línea.
>>> print archivo.read(2)
# leer como máximo 2 bytes
Es
>>> print archivo.readline(4)
# leer 1 línea completa o 4 bytes,
lo que ocurra primero.
Esto
>>> for linea in archivo:
# con un bucle for. Esta
es la forma recomendada
de lectura por líneas.
...
print linea
...
Esto es
# Notar la línea vacía. Esto es
porque la cadena leída incluye
un salto de línea al final, y
print agrega otro.
una prueba
de lectura!
Es importante notar que los ejemplos anteriores no funcionan en secuencia tal cual fueron escritos. Esto es
debido a que una vez que se lee contenido del archivo, la siguiente lectura comenzará desde donde acabó la
anterior. Es decir, si leemos 4 bytes de la primera línea (“Esto”), una siguiente lectura de 3 bytes devolverá ”
es”. Todo archivo contiene un puntero interno que actúa como un cursor o como un punto de partida para las
funciones de lectura o escritura y a su vez cada vez que se usa una de estas funciones, el puntero interno se
mueve
Para manipular el puntero interno existen los métodos seek, que recibe como parámetro la posición a la que
debe mover el puntero (0 para moverlo hacia el principio del archivo) y tell, que devuelve la posición actual del
puntero.
ESCRITURA
Si lo que queremos es escribir en el archivo, tenemos los métodos write ywritelines. Contrapartes
de read y readlines respectivamente, write escribe una cadena al archivo y writelines recibe una lista de
líneas para escribir. Por ejemplo, si quisiéramos recrear el archivo prueba.txt del ejemplo anterior, podemos
hacerlo de dos formas:
>>> archivo = open('prueba.txt', 'w')
# escritura y truncado
>>> archivo.write("Esto es\nuna prueba\nde lectura!")
>>> archivo.writelines(['Esto es\n', 'una prueba\n', 'de lectura!'])
# notar la inclusión explícita de saltos de línea
>>> archivo.close()
CIERRE
Cuando terminamos de trabajar con el archivo, lo cerramos con close(). Esto libera el archivo para ser usado por
otros programas, y además asegura que los cambios sobre él se guarden. De más está decir que Python se
encarga de cerrar todos los archivos que queden abiertos al final del programa, pero es una buena práctica no
dejar nada al azar y cerrar el archivo tan pronto como se lo termina de usar.
WITH
A partir de Python 2.5, podemos simplificar un poco el código necesario para abrir y cerrar el archivo
usando with:
with open('prueba.txt') as archivo:
for linea in archivo:
....
Esto nos libera de tener que cerrar el archivo explícitamente, ya que Python se encargará de cerrarlo
automáticamente al salir del bloque.