Maestros Python

PROGRAMACIÓN ORIENTADA A OBJETOS EN PYTHON Python es uno de los lenguajes de programación multiparadigma, más potente y

Views 99 Downloads 5 File size 2MB

Report DMCA / Copyright

DOWNLOAD FILE

Recommend stories

Citation preview

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

Abre el editor de textos Copia el código que está más abajo Guarda el archivo como presupuesto.py Abre una terminal/consola

5. Navega hasta el directorio donde haz guardado el archivo 6. Ahora escribe python presupuesto.py y ve como funciona. 7. 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 txt txt txt txt txt txt txt txt txt txt txt txt txt

= '\n'+self.divline+'\n' += '\t'+self.encabezado_nombre+'\n' += '\tWeb Site: '+self.encabezado_web+' | ' += 'E-mail: '+self.encabezado_email+'\n' += self.divline+'\n' += '\t'+self.titulo+'\n' += self.divline+'\n\n' += '\tFecha: '+self.fecha+'\n' += '\tEmpresa: '+self.empresa+'\n' += '\tCliente: '+self.cliente+'\n' += self.divline+'\n\n' += '\tDetalle del servicio:\n' += '\t'+self.servicio+'\n\n' += '\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 y recuerda que toda duda será resuelta en los comentarios.

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): File "", line 1, in File "presupuesto.py", line 158, in presupuesto = ModeloDePresupuesto() File "presupuesto.py", line 152, in __init__ self.set_datos_basicos() File "presupuesto.py", line 45, in set_datos_basicos self.seleccionar_plan() File "presupuesto.py", line 59, in seleccionar_plan elegir_plan = int(elegir_plan)

ValueError: invalid literal for int() with base 10: '' >>> 4. Intenta resolverlo tu mismo ¿cómo? a. lee la descripción del error y piensa como se produjo (es decir, cuál fue el paso previo) b. verifica la línea de código donde se produjo el error ¿sintaxis? c. Compara el código con el del ejemplo. ¿Lo ves igual? Entonces no pierdes nada, copiándolo textualmente de nuevo. d. ¿Sigue fallando? busca el error en Internet (¿Google?) 5. Si no logras resolverlo, busca ayuda: aquí, en foros o listas de correo, PERO, previamente ten en cuenta: a. Copia el error completo, tal cual se muestra y colócalo como referencia en tu post b. 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/ c. 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: ')

raw_input() 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: 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 Float() 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.

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

sumar, utilizamos + restar utilizamos dividir flotantes utilizamos / dividir enteros utilizamos // multiplicar utilizamos * obtener el resto de una división utilizamos % 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):

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.

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

RECUERDA: Si encuentras el error (o los errores), no lo postees hasta que al menos 5 personas hayan indicado que pudieron corregirlo.

LISTAS, TUPLAS, DICCIONARIOS Y ESTRUCTURAS DE CONTROL ¡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 2.

3. 4. 5. 6. 7.

antes) 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 Calculará automáticamente el número de presupuesto, y Finalmente nos preguntará si deseamos guardar el archivo: a. 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) b. Si elegimos “N” (no), nos mostrará el presupuesto en pantalla como lo hacía hasta ahora c. 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 2. Aquí almacenaremos todos los archivos del programa 3. Dentro de la carpeta “capitulo3″, crea una subcarpeta

llamadapresupuestos 4. Aquí se guardarán todos los presupuestos generamos en formato HTML (los que decidamos guardar) 5. Dentro de la carpeta “capitulo3″, crea otra subcarpeta llamada templates 6. Aquí guardaremos la plantilla HTML y otra con formato solo texto (TXT) 7. Dentro de la carpeta templates guarda los archivos template.html ytemplate.txt 8. Pulsa en cada archivo para descargarlos desde Launchpad. Estas serán, nuestras dos plantillas 9. Dentro de la carpeta capitulo3, crea un archivo llamado contador.txt 10. Será la guía que utilizaremos para calcular incrementalmente nuestros números de presupuesto. 11. Abre el archivo contador.txt, edita el contenido (está vacío), escribe 100 y guarda los cambios 12. Inicializamos el contador en 100. Nuestro primer presupuesto, obtendrá el número 101 y así sucesivamente de forma incremental. 13. Dentro de la carpeta capitulo3, guarda el módulo presupuesto.py(modificado) 14. 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 mi_tupla = ('texto', 100, 25.83, False) Crear 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: dict = {'clave':'dato', 'otra_clave':155} Crear 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

nuevo = ultimo_num+1

# genero nuevo

contador.seek(0)

# muevo cursor a

nuevo = str(nuevo)

# convierto numero

contador.write(nuevo)

# sobreescribo el

contador.close() self.numero_presupuesto = nuevo

# cierro contador # seteo el nuevo

numero número byte 0 a string número

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 byte indicado en el archivo; y objeto.close() para cerrar 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: 3. from string import Template 4. Guardar el contenido de la plantilla en una variable: 5. # Crear un objeto archivo: Abrir el archivo en modo de solo lectura

archivo = open('archivo', 'r') # Leer el archivo contenido = archivo.read() # No olvidarse de cerrar el archivo archivo.close()

6. Crear un diccionario de datos con dict(): 7. diccionario = dict(id='un valor', otro_id='Otro valor') 8. Hacer un render de datos con la clase Template y el método

substitute()o safe_substitute() (este último, es mejor para prevenir errores): 9. Template(plantilla).safe_substitute(diccionario) 10. 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 b # si a es mayor que b if a >= b # si a es mayor o igual que b if a < b # si a es menor que b if a 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: ○ Haremos que tanto la fecha actual como la de caducidad de nuestro presupuesto, se calculen de forma automática. ○ Al finalizar la carga de un presupuesto, tendremos la opción de abrirlo directamente en el navegador para visualizarlo. ○ 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: ○ Hacer nuestro código más legible. ○ Lograr que el programa tenga un mejor rendimiento. ○ 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. Abre una terminal 2. Navega hasta el directorio donde tienes almacenados los módulos del sistema

3. Ejecuta el intérprete interactivo de python (escribe: python) 4. 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>>> cortar aquí >> 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 if __name__ == "__main__": 75 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 def crear_archivo(ruta, contenido): 48 """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 return 'Error' 57 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:

ACCESO A BASES DE DATOS CON MYSQLDB 2. 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. 3. ¿Qué necesitarás para seguir este capítulo? 4. Los siguientes puntos serán necesarios para entender el capítulo, ya que no serán explicados: ○ Tener MySQL instalado ○ Conocer mínimamente la sintaxis del lenguaje SQL

TDD EN PYTHON CON UNITTEST 6. 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. 7. ¿Qué necesitarás para seguir este capítulo? 8. Los siguientes puntos serán necesarios para entender el capítulo, ya que no serán explicados: ○ Ganas de programar profesionalmente y estudiar algo que cuesta aprender solo.

INTERFACES GRÁFICAS PARA APLICACIONES DE ESCRITORIO EN PYTHON 10. 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.

11. ¿Qué necesitarás para seguir este capítulo? 12. Los siguientes puntos serán necesarios para entender el capítulo, ya

que no serán explicados: ○ ¿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 por única vez en mi vida la voluntad de “las masas”, en el capítulo de hoy aprenderemos 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! [apagar modo irónico]

HOLA MUNDO “EXTENDED” Haremos un “Hola Mundo” con wxPython, pero con algo más que un simple “Hola Mundo”. Nuestro programa 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 Importar wxPython: import wx Crear una clase extendida de wx.Frame: class MiClase(wx.Frame) 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) 6. Finalmente, crear una instancia de wx.App: 7. app = wx.App(False) 8. Otra instancia de nuestra clase: 9. ventana = MiClase(None, “Título de la ventana”) 10. Llamar a MainLoop del objeto app para mantener activo el programa: 11. app.MainLoop() 1. 2. 3. 4. 5.

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 2. 3. 4. 5. 6. 7.

MySQLdb) import MySQLdb Conectarse a la base de datos db_host = 'localhost' usuario = 'root' clave = 'clave'

8. base_de_datos = 'mi_basededatos' 9. db = MySQLdb.connect(host=db_host, user=usuario,

passwd=clave, 10. db=base_de_datos) 11. Abrir un cursor 12. cursor = db.cursor() 13. Ejecutar una consulta 14. 15. mi_query = "SELECT campo FROM tabla WHERE campo='valor'

ORDER BY campo" 16. cursor.execute(mi_query) 17. Si se está agregando, editando o eliminando un registro: hacer un commit a la base de datos 18. db.commit() 19. Si se están leyendo datos: obtener todos los registros hallados 20. cursor.fetchall() 21. u obtener solo el primero: 22. cursor.fetchone() 23. Cerrar el cursor abierto 24. 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-vista-controlador). 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.

Y por supuesto, para todos, un ¡EXCELENTE 2012!