Sei sulla pagina 1di 339

.

Inmersin en Python 3 o por Mark Pilgrim Copyright c 2009. Traduccin al espaol: Jos Miguel Gonzlez Aguilera o n e a Copyright de la traduccin c 2009. o Website de la traduccin: http://code.google.com/p/inmersionenpython3 o Agradecimientos del Traductor: A Mark Pilgrim. A Nieves, Alba y a Miguel. Licencia:

Este trabajo est licenciado bajo la licencia de Reconocimiento-No comercial-Compartir a bajo la misma licencia Creative Commons 3.0 Espaa. Para ver una copia de esta n licencia, visita http://creativecommons.org/licenses/by-nc-sa/3.0/es/ o env una a carta a Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. A continuacin se muestra un resumen de la licencia. o Usted es libre de: Compartir copiar, distribuir y comunicar pblicamente la obra u Rehacer hacer obras derivadas Bajo las condiciones siguientes: Reconocimiento. Debe reconocer los crditos de la obra de la manera especicada e por el autor o el licenciador (pero no de una manera que sugiera que tiene su apoyo o apoyan el uso que hacer de su obra). No comercial. No puede utilizar esta obra para nes comerciales.

Compartir bajo la misma licencia. Si altera o transforma esta obra, o genera una obra derivada, slo puede distribuir la obra generada bajo una licencia o idntica a sta. e e Al reutilizar o distribuir la obra, tiene que dejar bien claro los trminos de la licencia e de esta obra. Alguna de las condiciones puede no aplicarse si se obtiene el permiso del titular de los derechos de esta obra. Nada en esta licencia menoscaba o restringe los derechos morales del autor.

Cap tulo -1 Novedades de Inmersin en o Python 3


No es de aqu de donde venimos? Pink Floyd, The Wall

-1.1.

Alias Bajo el nivel del mar

Posiblemente hayas le el libro original Dive into Python y puede que hasta do lo hayas comprado. (Si es el caso: gracias!) Ya conoces bastante el lenguaje Python. Ests preparado para dar el salto a Python 3. . . . Si lo dicho es cierto, sigue leyendo. a (Si no es as tal vez sea mejor que comiences desde el principio en el cap , tulo ??). Python 3 viene con un script denominado 2to3. Aprende a usarlo y a quererlo. El apndice ?? es una referencia sobre las cosas que la herramienta 2to3 puede e arreglar automticamente en la conversin del cdigo de la versin 2 a la 3 de python. a o o o Puesto que muchas cosas son cambios de sintaxis, una buena forma de comenzar es aprender estas diferencias. Por ejemplo: print ahora es una funcin. . . o El caso de estudio del cap tulo ?? documenta mi esfuerzo (al n cumplido con xito!) de convertir una librer real de Python 2 a Python 3. Puede servirte o no. Es e a un ejemplo complejo de entender puesto que en primer lugar tienes que comprender algo el funcionamiento de la librer de forma que puedas entender lo que deja de a, funcionar y como lo arregl. Mucho de lo que se rompi al pasar a la versin 3 de e o o Python fue por causa de las cadenas. Por cierto, hablando de cadenas. . . Cadenas. U!. Por dnde podr empezar. Python 2 ten cadenas y cao a a denas unicode. Python 3 tiene bytes y cadenas. Lo que signica que todas las

CAP ITULO -1. NOVEDADES DE INMERSION EN PYTHON 3

cadenas ahora son unicode, y si quieres trabajar con un puado de bytes tienes que n usar el tipo bold bytes. Python 3 nunca convertir impl a citamente entre cadenas y bytes, por lo que si no estas seguro de lo que contiene una variable en un momento dado, el cdigo o seguro que fallar en algn momento. Lee el cap a u tulo 4 sobre cadenas para conocer los detalles. La divisin entre bytes y cadenas surgir en diversas partes del libro: o a 1. En el cap tulo 11 dedicado a los cheros, aprenders la diferencia entre leer a cheros en modo binario o en modo texto. La lectura (y escritura) de cheros en modo texto requiere que se utilice el parmetro encoding. Existen mtodos a e que cuentan los caracteres de un chero y mtodos que cuentan bytes. Si el e cdigo asume que un carcter es igual a un byte, no funcionar cuando el o a a 1 chero contenga caracteres multibyte . 2. En el cap tulo ?? dedicado a los servicios web n http, se muestra el mdulo o httplib2 que lee cabeceras y datos de HTTP. Las cabeceras se obtienen como cadenas, pero el contenido del cuerpo se obtiene como bytes. 3. En el cap tulo ?? aprenders el motivo por el que el mdulo pickle de Python a o 3 dene un formato de datos nuevo que es incompatible con Python 2 (Pista: Se debe a los bytes y cadenas). Tambin afecta al mdulo JSON, que no es e o capaz de manejar el tipo bytes. Te ensear como salvar este escollo. n e 4. En el cap tulo ?? sobre la conversin de la librer chardet a Python 3 se o a ver que la mayor parte de los problemas de conversin provienen de los bytes a o y cadenas. Incluso aunque no tengas inters en Unicode, que tendrs!, querrs leer sobre e a a el formateo de cadenas en Python 3 en el cap tulo ??, que es completamente diferente a Python 2. Los iteradores estn en todas partes en Python 3, y ahora los entiendo mucho a mejor que hace cinco aos cuando escrib Inmersin en Python. Debes comprenn o derlos t tambin, puesto que muchas funciones que anteriormente retornaban listas u e ahora, en Python 3, devuelven iteradores. Como m nimo, deber leer la segunda as parte del cap tulo ?? dedicado a los iteradores y la segunda parte del cap tulo ?? sobre el uso avanzado de los iteradores. Por peticin popular, he aadido el apndice ?? sobre nombres de mtodo o n e e especiales que guarda cierta similitud con el apartado similar de la documentacin o ocial de Python 3 pero con cierta iron a.
1

En unicode muchos caracteres se representan utilizando ms de un byte a

-1.1. ALIAS BAJO EL NIVEL DEL MAR

Cuando estaba escribiendo Inmersin en Python todas las librer de XML o as disponibles eran bastante malas. Entonces Fedrik Lundh escribi bold Elemento Tree, que es todo lo contrario a lo existente anteriormente. Los dioses de Python, actuando inteligentemente, incorporaron ElementTree a la librer estndar. Ahora a a esta librer es el fundamento del cap a tulo 12 sobre XML. Los viejos mtodos para e recorrer XML estn an disponibles, pero deber evitarlos, apestan! a u as Algo que es tambin nuevo no en el lenguaje, pero s en la comunidad es e la creacin de repositorios de cdigo como el o o ndice de paquetes de python (PyPI). Python dispone de utilidades para empaquetar el cdigo en formatos estndares y o a distribuirlos en PyPI. Lee el cap tulo ?? sobre cmo empaquetar librer en Python. o as

CAP ITULO -1. NOVEDADES DE INMERSION EN PYTHON 3

Cap tulo 0 Instalacin de Python o


Nivel de dicultad:     Tempora mutantur nos et mutamur in illis (Los tiempos cambian, y nosotros cambiamos con ellos) antiguo proverbio romano

0.1.

Inmersin o

Bienvenido a Python 3. Vamos a mojarnos! En este cap tulo, vas a instalar la versin de Python adecuada para ti. o

0.2.

Cul es la versin adecuada para ti? a o

Lo primero que necesitas hacer es instalar Python 3. Si ests utilizando una sesin en un servidor remoto (posiblemente a travs a o e de Internet), el administrador del servidor puede que ya lo haya instalado por ti. Si ests utilizando Linux1 en casa, puede que tambin lo tengas ya instalado, aunque a e 2 actualmente la mayor parte de las distribuciones de Linux vienen con Python 2 instalado (como vers en este cap a tulo, puedes tener simultneamente ms de una a a versin de Python en tu ordenador sin problemas). En los Mac OS X se incluye una o versin de l o nea de comando de Python 2, pero no Python 3. Microsoft Windows no
Nota del Traductor: El nombre correcto del sistema operativo Linux es GNU/Linux, no obstante, por comodidad, en este libro se utilizar unicamente Linux para mayor comodidad a 2 ao 2009 n
1

CAP ITULO 0. INSTALACION DE PYTHON

trae ninguna versin de Python. Pero no te preocupes! siempre puedes instalarlo o t mismo, tengas el sistema operativo que tengas. u La forma ms sencilla para comprobar si tienes instalado Python 3 en tu a sistema Linux o Mac OS X es abrir un terminal de l nea de comandos. Para ello debes hacer lo siguiente: Si ests en Linux, busca en el men de Aplicaciones un programa denominado a u terminal (puede estar en un submen, posiblemente Accesorios o Sistema). u Si ests en Mac OS X, existe una aplicacin que se llama Terminal.app en la a o carpeta /Aplicaciones/Utilidades/. Una vez te encuentres en la l nea de comando3 , teclea python3 (en minsculas u y sin espacios) y observa lo que sucede. En mi sistema Linux, Python 3 ya est insa talado, por lo que el resultado de ejecutar este comando hace que el terminal entre en la consola4 interactiva de Python.
jmgaguilera@acerNetbook-jmga:~$ python3 Python 3.0.1+ (r301:69556, Apr 15 2009, 15:59:22) [GCC 4.3.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>>

(Para salir de la consola interactiva de Python escribe exit() y pulsa la tecla INTRO.) Al ejecutar esta misma sentencia python3 en un ordenador Linux que no tenga instalado Python 3 el mensaje que se obtendr ser parecido al siguiente: a a
jmgaguilera@acerNetbook-jmga:~$ python3 bash: python3: orden no encontrada jmgaguilera@acerNetbook-jmga:~$ python3

Bueno, volviendo ahora a la pregunta sobre cul es la versin de Python 3 a o apropiada para ti, queda claro que es aquella que se ejecute en el ordenador que tengas. Para conocer cmo instalar Python 3, contina leyendo en el apartado que o u corresponda a tu sistema operativo.
3 4

Tambin conocido como el prompt e En ingls shell e

0.3. INSTALACION EN MICROSOFT WINDOWS

0.3.

Instalacin en Microsoft Windows o

Windows se ejecuta actualmente en dos plataformas diferentes: 32 y 64 bits. Asimismo, existen diferentes versiones de Windows XP, Vista, Windows 7 y Python 3 funciona en todas ellas. Es ms importante, con vistas a la instalacin, la a o distincin que existe entre los dos tipos de arquitecturas. Si no sabes de qu tipo es o e la arquitectura de tu ordenador, lo ms probable es que sea de 32 bits. a Visita python.org/download/ para descargar la aplicacin de instalacin de o o Python 3 que sea correcta para para la arquitectura de tu ordenador. Las posibilidades sern parecidas a: a Python 3.*.* x86 Windows installer (Windows binary does not include sources) Python 3.*.* AMD64 Windows installer (Windows AMD64 binary does not include sources) La descarga exacta var en funcin de las actualizaciones. Por eso he puesto a o asteriscos en lugar del nmero de versin. Deber instalar siempre la ultima versin u o as o disponible de Python 3.x a menos que tengas alguna razn importante para no o hacerlo.

Figura 1: Advertencia al inicio

Cuando la descarga nalize, pulsa (doble click) sobre el chero .msi que has descargado. Windows mostrar una alerta de seguridad (gura 1) para avisarte de a que ests intentando ejecutar un chero que instalar cosas en tu ordenador. El a a chero instalador de Python est rmado electrnicamente por la Python Software a o

CAP ITULO 0. INSTALACION DE PYTHON

Foundation, que es la organizacin sin animo de lucro que supervisa el desarrollo de o Python. No aceptes imitaciones! Pulsa el botn Run o Ejecutar5 para que se inicie la ejecucin del programa o o instalador de Python.

Figura 2: Tipo de instalacin o

Lo primero que pide el programa instalador (gura 2) es que le indiques si quieres instalar Python 3 para todos los usuarios del ordenadores o unicamente para ti. Por defecto aparece seleccionada la opcin Instalar para todos los usuarios, que o es la mejor opcin, a no ser que tengas una buena razn para no hacerlo6 . o o Cuando hayas seleccionado la opcin deseada, pulsa el botn Next o Siguiente o o para continuar con la instalacin. o Lo siguiente que pedir el instalador (gura 3) es que le digas el directorio a de instalacin. El valor por defecto para todas las versiones de Python 3.1.x es o C:zPython31z, que es un valor adecuado para la mayor de los usuarios. Salvo que a tengas una razn espec o ca para cambiarlo, como por ejemplo, que mantengas una unidad separada para la instalacin de aplicaciones, puedes usar este directorio para o instalar Python. Para cambiar el directorio de instalacin, puedes utilizar las opciones de pano
depender del idioma en el que se encuentre tu sistema operativo a Una posible razn por la podr querer instalarlo unicamente para tu usuario es que estuvieras o as instalando Python en el ordenador de la empresa y no tengas permisos de administrador en tu cuenta de usuario. Pero en ese caso, qu haces instalando Python sin permiso del administrador e de tu empresa? A m no me metas en problemas, eso es cosa tuya.
6 5

0.3. INSTALACION EN MICROSOFT WINDOWS

talla o, simplemente, teclear el directorio deseado (con el path completo) en la caja de texto.

Figura 3: Directorio de instalacin o

Puedes instalar Python en el disco duro en el lugar que desees. Cuando hayas nalizado, pulsa el botn Next o Siguiente para continuar. o

Figura 4: Seleccin de elementos a instalar o

La siguiente pantalla (gura 4) parece ms compleja, pero en realidad no lo a es. Como pasa con otros muchos instaladores, te ofrece la opcin de que seleccioo

10

CAP ITULO 0. INSTALACION DE PYTHON

nes qu cosas concretas quieres instalar. Puedes instalar todos los componentes de e Python 3, y si el espacio en disco es justo, puedes excluir ciertos componentes. Registrar las extensiones. Si seleccionas esta opcin, el instalador modio car la conguracin de Windows para que te permita ejecutar los scripts7 de a o Python con solo hacer doble click sobre el chero. Esta opcin no necesita de o espacio en disco, por lo que no tiene mucho sentido no marcarla. TclzTk es la librer grca que utiliza la consola de Python. La usaremos a a a lo largo de todo el libro, por lo que es muy recomendable que la mantengas entre los componentes a instalar. Documentacin instala un chero de ayuda que contiene gran parte de la o informacin que se encuentra en docs.python.org. Es recomendable instalar o esta opcin cuando es previsible que no dispongas de conexin permanente a o o Internet. Scripts de utilidades. Estos scripts incluyen diversas utilidades, entre ellas el script 2to3.py sobre el que hablaremos ms adelante. Es necesaria si vas a a migrar cdigo de Python 2 a Python 3. Si no dispones de cdigo para migrar o o puedes saltarte esta opcin. o Suite de pruebas. Es una coleccin de scripts que se utilizan para probar o el buen funcionamiento del intrprete de Python. En este libro no lo vamos a e usar, yo no lo he usado jams en el largo tiempo que llevo programando en a Python. Es totalmente opcional su instalacin. o Si no ests seguro de cuando espacio en disco tienes libre, pulsa el botn Disk a o Usage. El instalador te mostrar las unidades de disco (gura 5) y el espacio libre a disponible en cada una de ellas, as como el espacio que quedar despus de la a e instalacin. o Cuando termines la comprobacin, pulsa el botn OK para volver a la pantalla o o anterior. Si decides excluir alguna opcin (gura 6), selecciona el botn desplegable o o que aparece a la izquierda del texto de la opcin y selecciona Entire feature will be o unavailable. Por ejemplo, si excluyes la suite de pruebas ahorrars 7908 KBytes de a espacio en disco. Pulsa el botn Next para conrmar tu seleccin de opciones. o o
7

cheros que contienen sentencias de Python, que normalmente tienen la extensin .py o

0.3. INSTALACION EN MICROSOFT WINDOWS

11

Figura 5: Espacio libre

Figura 6: Excluir una opcin o

El instalador copiar todos los cheros (gura 7 al directorio de destino que a hayas seleccionado (Suele ser tan rpido, que tuve que probarlo tres veces antes de a conseguir sacar una foto de la pantalla mostrndolo). a Por ultimo, pulsa el botn Finish para salir del instalador (gura 8). o Si ahora buscas en el men de Inicio, deber encontrar un nuevo elemento u as denominado Python 3.1. Dentro de esta nueva opcin de men encontrars dos proo u a

12

CAP ITULO 0. INSTALACION DE PYTHON

Figura 7: Instalacin o

Figura 8: Instalacin completada o

gramas denominados Python e IDLE. Selecciona uno de estos dos elementos para ejecutar la consola interactiva de Python (gura 9). Contina en el apartado 0.7 u

0.4. INSTALACION EN UN MAC OS X

13

Figura 9: Instalacin completada o

0.4.

Instalacin en un Mac OS X o

Todos los ordenadores Apple Macintosh modernos utilizan procesadores de Intel8 Los Macintosh antiguos utilizaban procesadores Power PC. No es necesario que conozcas esta diferencia puesto que unicamente existe un instalador para todos los tipos de Macs. Visita python.org/download/ para descargar la aplicacin de instalacin de o o Python 3 para Mac. Debes buscar un enlace cuyo nombre sea algo as como Mac Installer Disk Image (3.*.*. El nmero de versin puede variar, pero asegrate u o u de descargar una versin de Python 3 y no de Python 2. o Tu navegador deber montar de forma automtica esta imagen de disco y a a abrir una ventana de Finder para mostrarte el contenido de la imagen. Si no fuese as debers buscar la imagen de disco en el directorio de descargas y hacer doble click , a sobre ella para que se cargue. El nombre de la imagen de disco ser algo as como a python-3-1.dmg. Una vez tengas visible en pantalla el contenido de la imagen de disco (gura 10), podrs observar que contiene varios cheros de texto (Build.txt, a License.txt, ReadMe.txt), y el el chero del paquete de instalacin Python.mpkg. o
8

Como la mayor de ordenadores con Windows a

14

CAP ITULO 0. INSTALACION DE PYTHON

Figura 10: Finder: contenido de la imagen de disco

Haz doble click con el cursor sobre el chero de instalacin Python.mpkg para o iniciar el instalador de Python para Mac.

Figura 11: Bienvenida a la instalacin o La primera pgina (gura 11) que muestra el programa de instalacin describe a o de forma concisa qu es Python, y remite al chero ReadMe.txt (que seguramente e no te le verdad?) por si deseas conocer ms detalles. ste a Pulsa el botn Continue para avanzar en la instalacin. o o La siguiente pantalla (gura 12) muestra informacin importante: Python neo cesita que tengas instalado Mac OS X 10.3 o superior. Si ests ejecutando una versin a o de Mac OS X 10.2 o anterior, deber actualizar tu ordenador a ultima versin. Una as o de las razones ms convincentes, es que Apple ya no proporciona actualizaciones de a seguridad para tu versin del sistema operativo, por lo que tu ordenadores est en o a riesgo cada vez que est conectado a Internet. Otra razn, no menos convincente, es a o que no puedes ejecutar Python 3.

0.4. INSTALACION EN UN MAC OS X

15

Figura 12: Informacin importante o

Pulsa el botn Continue para avanzar en la instalacin. o o

Figura 13: Licencia

Como todos los buenos instaladores, lo siguiente que el instalador de Python muestra es la pantalla de aceptacin de la licencia (gura 13). Python es Open o Source (software de fuentes abiertas) cuya licencia cuenta con la aprobacin de la o iniciativa de Cdigo Abierto. Python cuenta con un cierto nmero de propietarios y o u patrocinadores a lo largo de su historia, cada uno de los cuales ha dejado su marca en la licencia. Pero el resultado nal es este: Python es Cdigo Abierto, y puedes o usarlo en cualquier plataforma, para lo que desees, sin necesidad de pagar ningn u canon, ni obligacin, ni nada a cambio. o Pulsa el botn Continue de nuevo para avanzar en la instalacin. o o Debido a las peculiaridades del proceso de instalacin estndar de Apple, es o a

16

CAP ITULO 0. INSTALACION DE PYTHON

Figura 14: Aceptacin de la Licencia o

necesario que aceptes la licencia (gura 14) para que el instalador te permita continuar. Puesto que Python es Cdigo Abierto, en realidad ests aceptando una licencia o a que te garantiza derechos adicionales, en lugar de quitrtelos. a Pulsa el botn Agree para continuar. o La siguiente pantalla (gura 15) te permite modicar la ubicacin en la que o se efectuar la instalacin. Debes instalar Python en el disco de arranque, pero a o debido a ciertas limitaciones en el instalador, ste no te obliga a ello, por lo que e ten cuidado!. En realidad, yo nunca he tenido la necesidad de cambiar la ubicacin o de instalacin, por ello, salvo causa justicada, acepta la ubicacin sugerida por el o o instalador.

Figura 15: Seleccin de la ubicacin o o

Desde esta pantalla tambin puedes modicar la instalacin con el n de que no e o

0.4. INSTALACION EN UN MAC OS X

17

se instalen algunas funcionalidades. Si quieres hacer esto pulsa el botn Customize, o en caso contrario pulsa el botn Instalar. o

Figura 16: Personalizacin de la instalacin o o

Si eliges una instalacin personalizada (has pulsado el botn Customize), el o o instalador te muestra (gura 16) una pantalla con una lista de caracter sticas: Python Framework. Es el ncleo de Python, por lo que est seleccionado y u a deshabilitado con el n de que no puedas cambiarlo. Aplicaciones GUI incluye IDLE, la consola interactiva grca de Python a que usaremos a lo largo de todo el libro. Te recomiendo encarecidamente que mantengas esta opcin seleccionada. o Herramientas de l nea de comandos, que incluyen la aplicacin python3. o Tambin te recomiendo que mantegas esta opcin seleccionada. e o Documentacin de Python, que contiene mucha de la informacin dispoo o nible en docs.python.org. Muy recomendables si tienes previsto estar desconectado de Internet. Actualizador del perl de la consola, que controla si actualizas tu perl de consola (utilizado por la aplicacin Terminal.app) con el n de que la versin o o de Python que ests instalando se encuentre en el camino de bsqueda de la a u consola. Para los propsitos de este libro, esta opcin no es necesario que la o o instales. Actualizar la versin de Python del sistema. Esta opcin no deber o o a modicarse. Le dice a tu ordenador Mac que utilice Python 3 como versin o

18

CAP ITULO 0. INSTALACION DE PYTHON por defecto para todos los scripts, incluido aquellos que vienen con el sistema operativo. Seleccionar esta opcin podr producir efectos muy negativos en tu o a sistema, puesto que la mayor parte de los scripts del sistema operativo estn a escritos para Python 2, y pueden fallar en Python 3. Pulsa el botn Install para continuar. o

Figura 17: Solicitando derechos administrativos

Debido a que el instalador copia archivos binarios en /usr/local/bin/, antes de iniciar dicha copia se solicitan permisos de administrador mediante una pantalla (gura 17) en la que hay que teclear la clave del administrador del sistema. No es posible instalar Python en Mac sin disponer de las credenciales de administrador. Pulsa el botn OK para comenzar la instalacin. o o El instalador mostrar una barra de progreso (gura 18) mientras se instalan a las funcionalidades que hayas seleccionado. Si todo va bien, el instalador mostrar en pantalla (gura 19) una marca verde a para indicar que la instalacin de ha completado satisfactoriamente. o Pulsa el botn Close para salir del instalador. o Si no has cambiado la ubicacin de la instalacin, Python 3.1.* se habr inso o a talado en una carpeta denominada Python 3.1 (gura 20) dentro de la carpeta /Aplications. El elemento ms importante en ella es IDLE, que es la consola grca a a interactiva de Python. Haz doble click con el cursor sobre IDLE para ejecutar la consola de Python. La mayor parte del tiempo la pasars explorando Python mediante el uso de a

0.5. INSTALACION EN UBUNTU LINUX

19

Figura 18: Instalacin o

Figura 19: Fin de la instalacin o

esta consola (gura 21). Los ejemplos de este libro asumen que eres capaz de ejecutar esta consola en todo momento. Contina en el apartado 0.7 u

0.5.

Instalacin en Ubuntu Linux o

Las diferentes distribuciones existentes hoy d de Linux suelen disponer de a vastos repositorios de aplicaciones listas para instalar de forma sencilla. Los detalles exactos var en funcin de la distribucin de Linux. En Ubuntu Linux, la forma an o o ms sencilla de instalar Python 3 consiste en usar la opcin Aadir y quitar... del a o n men de Aplicaciones (gura 22). u

20

CAP ITULO 0. INSTALACION DE PYTHON

Figura 20: Carpeta Python

Figura 21: Consola grca a

Cuando ejecutas por primera vez el programa para Aadir/Quitar aplicacion nes, se muestra una lista de aplicaciones preseleccionadas en diferentes categor as. Algunas ya se encuentran instaladas en tu ordenador, pero la mayor no. Puesto a

0.5. INSTALACION EN UBUNTU LINUX

21

Figura 22: Aadir/Quitar aplicaciones n

que este repositorio consta de ms de 10.000 aplicaciones, encontrar la que se desea a puede ser dif para facilitar la labor es posible aplicar diferentes ltros que limicil, tan las aplicaciones que se muestran en la lista de pantalla. El ltro por defecto es aplicaciones mantenidas por Canonical que es el pequeo subconjunto formado n por aquellas apliicaciones que se mantienen ocialmente por parte de Canonical, la compa que distribuye y mantiene Ubuntu Linux. na Como Python 3 no est en este subconjunto de aplicaciones, el primer paso es a desplegar los ltros (Mostrar:) y seleccionar Todas las aplicaciones libres (gura 23).

Figura 23: Todas las aplicaciones libres

Despus puedes ltrar an ms utilizando la caja de texto de bsqueda con el e u a u n de buscar el texto Python 3 (gura 24). Ahora la lista de aplicaciones que se muestran se limita a aquellas que, de algn u modo, incluyen la cadena Python 3. Ahora debes marcar dos paquetes. El primero es Python (v3.0). Que contiene el intrprete de Python 3. e

22

CAP ITULO 0. INSTALACION DE PYTHON

Figura 24: Bsqueda de aplicaciones relacionadas con Python 3 u

Figura 25: Seleccin del paquete Python 3 o

El segundo paquete que hay que marcar se encuentra inmediatamente delante, IDLE (usando Python 3.0), que es la consola grca que usaremos a lo largo de todo a el libro (gura 26). Una vez hayas seleccionado los dos paquetes, pulsa el botn Aplicar cambios o para continuar. El gestor de paquetes solicitar que conrmes que quieres instalar tanto IDLE a (usando Python 3.0) como Python (3.0) (gura 27). Pulsa el botn Aplicar para continuar. o El gestor de paquetes te pedir que te identiques con la clave de usuario para a acceder a los privilegios administrativos que permiten instalar aplicaciones. Una vez hecho esto, el gestor de paquetes mostrar una pantalla (gura 28) con el grado a de avance de la instalacin mientras se descargan los paquetes seleccionados del o repositorio de Internet de Ubuntu Linux.

0.5. INSTALACION EN UBUNTU LINUX

23

Figura 26: Seleccin del paquete IDLE o

Figura 27: Conrmacin o

Cuando los paquetes se hayan descargado, el instalador iniciar automticaa a mente el proceso de instalacin en tu ordenador (gura 29). o Si todo va bien, el gestor de paquetes conrmar que ambos paquetes se instalaa ron satisfactoriamente (gura 30). Desde esta pantalla puedes ejecutar directamente IDLE haciendo doble click sobre l. O puedes pulsar el botn Cerrar para nalizar el e o gestor de paquetes. En cualquier caso, puedes lanzar la consola grca de Python siempre que a quieras seleccionando IDLE en el submen Programacin del men de Aplicaciones. u o u Es en la consola de Python (gura 31) donde pasars la mayor parte del tiempo a explorando Python. Los ejemplos de este libro asumen que eres capaz de ejecutar la consola de Python siempre que sea necesario. Contina en el apartado 0.7 u

24

CAP ITULO 0. INSTALACION DE PYTHON

Figura 28: Descarga de paquetes

Figura 29: Descarga de paquetes

Figura 30: Instalacin nalizada o

0.6. INSTALACION EN OTRAS PLATAFORMAS

25

Figura 31: Consola de Python en Ubuntu Linux

0.6.

Instalacin en otras plataformas o

Python 3 est disponible en otras muchas plataformas. En particular, est disa a ponible prcticamente en todas las distribuciones Linux, BSD y Sun Solaris. Por a ejemplo, RedHat Linux utiliza el gestor de paquetes yum; FreeBSD tiene su propia coleccin de paquetes, y Solaris tiene el gestor de paquetes pkgadd y otros. Una o rpida bsqueda en Internet de los trminos Python 3 + emphtu sistema operativo a u e te mostrar si existe un paquete de Python 3 disponible para tu sistema, y cmo a o instalarlo.

0.7.

Uso de la consola interactiva de Python

En la consola interactiva de Python puedes explorar la sintaxis del lenguaje, solicitar ayuda interactiva sobre las sentencias del lenguaje, y depurar programas cortos. La consola grca (denominada IDLE) tambin proporciona un editor de textos a e bastante decente que resalta mediante colores la sintaxis del lenguaje Python. Si no tienes an un editor de textos de tu eleccin, puedes darle una oportunidad a IDLE. u o Vamos a comenzar! La shell de Python es un estupendo lugar para comenzar

26

CAP ITULO 0. INSTALACION DE PYTHON

a jugar con el lenguaje Python de forma interactiva. A lo largo de este libro vers a un montn de ejemplos como este: o
>>> 1 + 1 2

Los tres s mbolos de mayor que, , representan el prompt 9 de Python. No teclees nunca estos tres caracteres. Se muestran para que sepas que este ejemplo se debe teclear en la consola de Python. Lo que tienes que teclear es 1 + 1. En la consola puedes teclear cualquier expresin o sentencia vlida del lenguaje. No seas t o a mido, no muerde! Lo peor que puede pasarte es que Python muestre un mensaje de error, si tecleas algo que no entiende. Las sentencias se ejecutan inmediatamente (despus de que pulses la tecla e INTRO); las expresiones se calculan en el momento, y la consola imprime en pantalla el resultado. 2 es el resultado de la expresin. Como 1 + 1 es una expresin vlida en el o o a lenguaje Python, al pulsar la tecla INTRO Python evala la expresine imprime el u o resultado, que en este caso es 2. Vamos a probar otro ejemplo.
>>> print(Hola mundo!) Hola mundo!

Muy sencillo, no? Pero hay muchas otras cosas que puedes hacer en la consola de Python. Si en algn momento te bloqueas no recuerdas una sentencia, o no recuerdas los u argumentos que debes pasar a una funcin determinada puedes obtener ayuda en o la propia consola. Simplemente teclea help y pulsa ENTER.
>>> help Type help() for interactive help, or help(object) for help about object.

Exiten dos modos de ayuda: Puedes solicitar ayuda de un objeto concreto, lo que muestra la documentacin o del mismo y vuelve al prompt de la consola de Python.
Nota del Traductor: El prompt es el indicador que usa una consola, en este caso la consola de Python, para que el usuario sepa que puede teclear alguna sentencia. Como el uso de la palabra prompt est tan extendido para este concepto, y no existe uno en espaol de amplio uso, en este a n libro se utilizar sin traducir. a
9

0.7. USO DE LA CONSOLA INTERACTIVA DE PYTHON

27

Tambin puedes entrar en el modo ayuda, en el que en lugar de evaluar expree siones de Python, puedes teclear palabras reservadas del lenguaje o nombres de sentencias y la consola imprime lo que sepa sobre ellas. Para entrar en el modo interactivo de ayuda teclea help() y pulsa INTRO.
>>>help() Welcome to Python 3.0! This is the online help utility.

If this is your first time using Python, you should definitely check out the tutorial on the Internet at http://docs.python.org/tutorial/. Enter the name of any module, keyword, or topic to get help on writing Python programs and using Python modules. To quit this help utility and return to the interpreter, just type "quit". To get a list of available modules, keywords, or topics, type "modules", "keywords", or "topics". Each module also comes with a one-line summary of what it does; to list the modules whose summaries contain a given word such as "spam", type "modules spam". help>

Observa que ahora el prompt cambia de a help. Este cambio sirve para recordarte que te encuentras en el modo de ayuda interactiva. Ahora puedes teclear cualquier palabra reservada, sentencia, nombre de mdulo, nombre de funcin casi o o cualquier cosa que Python entienda y leer la documentacin que haya disponible o sobre el tema tecleado.
help> print Help on built-in function print in module builtins: print(...) print(value, ..., sep= , end=\n, file=sys.stdout) Prints the values to a stream, or to sys.stdout by default. Optional keyword arguments: file: a file-like object (stream); defaults to the current sys.stdout. sep: string inserted between values, default a space. end: string appended after the last value, default a newline. help> Papaya no Python documentation found for Papaya

28
help> quit

CAP ITULO 0. INSTALACION DE PYTHON

You are now leaving help and returning to the Python interpreter. If you want to ask for help on a particular object directly from the interpreter, you can type "help(object)". Executing "help(string)" has the same effect as typing a particular string at the help> prompt. >>>

En el ejemplo anterior se obtiene en primer lugar la documentacin sobre la o funcin print. Para ello se ha tecleado en el modo ayuda la palabra print y luego se ha o pulsado INTRO. Como resultado se obtiene un texto en el que se muestra el nombre de la funcin, un breve resumen de la misma, los argumentos de la funcin y sus o o valores por defecto. Si la documentacin te parece demasiado opaca, no te asustes. o Aprenders lo necesario sobre todos estos conceptos en los prximos cap a o tulos de este libro. Evidentemente el modo de ayuda no lo sabe todo. Si tecleas algo que no sea una sentencia, mdulo, funcin u otra palabra reservada de Python,el modo de ayuda o o interactiva mostrar un mensaje indicando que no encuentra documentacin alguna a o para el concepto que hayas tecleado. Por ultimo, para salir del modo de ayuda unicamente tienes que teclear quit y pulsar INTRO. El prompt vuelve de nuevo a para indicar que has abandonado el modo de ayuda interactiva y que de nuevo te encuentras en la consola de Python. IDLE, adems de servir como consola grca de Python, incluye tambin un a a e editor de textos que conoce el lenguaje Python. Vers cmo usarlo en la seccin a o o siguiente.

0.8.

Editores de texto e IDEs para Python

IDLE no es el unico entorno existente para escribir programas en Python. Aun que es muy util para comenzar a aprender el lenguaje, muchos desarrolladores pre eren utilizar otros editores de texto o Entornos Integrados de Desarrollo 10 . No los voy a abarcar aqu unicamente comentar que la comunidad de Python mantiene , e una lista de editores para el lenguaje Python sobre diversas plataformas y licencias de software.
En ingls se suele hablar de IDE, para referirse a los Integrated Development Environment, que e son aplicaciones que permiten desarrollar de forma rpida al incluir un editor de textos, compilador, a depurador e incluso herramientas de diseo de aplicaciones avanzadas. n
10

0.8. EDITORES DE TEXTO E IDES PARA PYTHON

29

Tambin puede ser de inters para ti la lista de Entornos Integrados de Desae e rrollo para Python, aunque an son pocos los que sirven para Python 3. Uno de ellos u es PyDev, un plugin para Eclipse que convierte a Eclipse en un completo Entorno Integrado de Desarrollo para Python. Ambos, Eclipse y PyDev, son multiplataforma y de cdigo abierto. o Por la parte comercial, existe un entorno de desarrollo denominado Komodo IDE. Tiene una licencia que se paga por cada usuario, pero tambin ofrece descuento e para estudiantes, y una versin con licencia de prueba limitada. o Llevo programando en Python nueve aos, yo, para editar los programas, utin lizo GNU Emacs y los depuro en la shell de l nea de comando11 . No existe un modo correcto de desarrollar en Python. Encuentra lo que mejor se adapte a ti!

11

Nota del Traductor:En mi caso uso GVim y el depurador de consola pudb

30

CAP ITULO 0. INSTALACION DE PYTHON

Cap tulo 1 Tu primer programa en Python


Nivel de dicultad:     No entierres tu carga en un santo silencio. Tienes un problema? Estupendo. Algrate, e sumrgete en l e investiga. e e Ven. Henepola Gunarata

1.1.

Inmersin o

Los libros sobre programacin suelen comenzar con varios cap o tulos sobre los fundamentos y van, poco a poco, avanzando hasta llegar a hacer programas utiles. Vamos a saltarnos todo eso. Lo primero que vamos a ver es un programa Python completo. Probablemente no tenga ningn sentido para ti. No te preocupes por eso, u vamos a diseccionarlo l nea por l nea. Primero lelo y trata de interpretarlo. e
1 # parahumanos . py 2 3 SUFIJOS = { 1 0 0 0 : [ KB , MB , GB , TB , PB , EB , ZB , YB ] , 4 1 0 2 4 : [ KiB , MiB , GiB , TiB , PiB , EiB , ZiB , 5 YiB ] } 6 7 def tamanyo aproximado ( tamanyo , u n k i l o b y t e e s 1 0 2 4 b y t e s=True ) : 8 C o n v i e r t e un tama o de f i c h e r o en formato l e g i b l e por p e r s o n a s n 9 10 Argumentos / par metros : a 11 tamanyo tama o de f i c h e r o en b y t e s n 12 u n k i l o b y t e e s 1 0 2 4 b y t e s s i True ( por d e f e c t o ) , 13 usa m l t i p l o s de 1024 u 14 s i F a l s e , usa m l t i p l o s de 1000 u

31

32
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

CAP ITULO 1. TU PRIMER PROGRAMA EN PYTHON

retorna : string i f tamanyo < 0 : r a i s e V a l u e E r r o r ( e l n mero debe s e r no n e g a t i v o ) u m u l t i p l o = 1024 i f u n k i l o b y t e e s 1 0 2 4 b y t e s e l s e 1000 f o r s u f i j o in SUFIJOS [ m u l t i p l o ] : tamanyo /= m u l t i p l o i f tamanyo < m u l t i p l o : return { 0 : . 1 f } {1} . format ( tamanyo , s u f i j o ) r a i s e V a l u e E r r o r ( n mero demasiado grande ) u if name == m a i n : print ( tamanyo aproximado ( 1 0 0 0 0 0 0 0 0 0 0 0 0 , F a l s e ) ) print ( tamanyo aproximado ( 1 0 0 0 0 0 0 0 0 0 0 0 0 ) )

Antes de analizarlo paso a paso vamos a ejecutar el programa en la l nea de 1 comandos. En Linux o en Mac debes teclear: python3 parahumanos.py . El resultado ser parecido a lo siguiente: a
1 2 3 t u u s u a r i o @ t u o r d e n a d o r : / inmersionEnPython3$ python3 parahumanos . py 1 . 0 TB 9 3 1 . 3 GiB

En Windows debes teclear lo mismo: python3 parahumanos.py, unicamente var a la forma del prompt de la consola. El resultado ser parecido a: a
1 C: \ \ i n m e r s i o n e n p y t h o n 3 :> python3 parahumanos . py 2 1 . 0 TB 3 9 3 1 . 3 GiB

Qu ha pasado? Acabas de ejecutar tu primer programa Python. Has ejecutae do el intrprete de Python en la l e nea de comandos (python3), y le has pasado como parmetro el nombre del chero de script (parahumanos.py) que quer ejecutar. a as El chero de script, a su vez, dene una unica funcin de python, la funcin o o tamnyo aproximado, que toma como parmetros un tamao de chero con una precia n sin de bytes y calcula el tamao en una unidad mayor en la que el valor quede ms o n a bonito, a cambio, el resultado es aproximado. (El funcionamiento del Explorador de Windows; del Finder de Mac OS X, o de Nautilus, Dolphin o Thunar de Linux es muy parecido. Si muestras en cualquiera de ellos una carpeta de documentos en modo detalle, de forma que se vean en diferentes columnas, el icono del documento,
Para que funcione correctamente debes moverte al directorio en el que est grabado el chero e parahumanos.py.
1

1.2. DECLARACION DE FUNCIONES

33

nombre, tamao, tipo, fecha de ultima modicacin, etc. Observars que si un don o a cumento determinado ocupa 1093 bytes, en la columna de tamao no dir eso, sino n a que dir algo as como 1 KB. Esto es lo que hace la funcin tamanyo aproximado) a o Las l neas de cdigo print(tamanyo aproximado(argumentos)) del nal del script, o l neas 31 y 32, son dos llamadas a funciones primero se llama a la funcin tao manyo aproximado() pasndole unos parmetros (tambin llamados argumentos), a a e esta funcin se ejecuta y devuelve un resultado que, posteriormente, se pasa coo mo parmetro a la funcin print(). Todo ello en la misma l a o nea. La funcin print() es interna del lenguaje Python2 ; nunca vers una declaracin o a o expl cita de ella. La puedes usar cuando quieras, en cualquier parte de un programa Python3 . Porqu la ejecucin del script en la l e o nea de comandos retorna siempre la misma respuesta? Lo veremos ms adelante. Primero vamos a ver el funcionamiento a de la funcin tamanyo aproximado(). o

1.2.

Declaracin de funciones o

Python dispone de funciones como la mayor de los lenguajes, pero no tiene a cheros de cabecera como c++ o secciones de interface/implementation como en Pascal. En Python unicamente hay que declarar la funcin, como en el siguiente o ejemplo:
1 def tamanyo aproximado ( tamanyo , u n k i l o b y t e e s 1 0 2 4 b y t e s=True ) :

La palabra reservada def inicia la declaracin de la funcin, seguida del nomo o bre que le quieres dar a la misma, seguida de los parmetros de la funcin entre a o parntesis. Separndolos por comas en caso de que sean varios parmetros. e a a Observa tambin que, en Python, las fune ciones no denen un tipo de datos de retorno. No se especica el tipo de datos del valor que retornan las funciones. Es ms, ni siquiera se esa pecica si se retorna o no un valor.

En Python cuando necesitas una funcin, solamente tienes o que declararla.

En realidad, todas las funciones de Python tienen un valor de retorno; si dentro del cdigo de la funcin se ejecuta una sentencia o o return, el valor que acompaa a la sentencia ser el valor de retorno, en caso contrario n a se retorna el valor None, que es la forma de expresar el vac (null) en Python. o
En ingls built-in. e Existen montones de funciones internas del lenguaje, y muchas ms que estn separadas en a a mdulos. Lo veremos poco a poco, ten paciencia, pequeo saltamontes. o n
3 2

34

CAP ITULO 1. TU PRIMER PROGRAMA EN PYTHON En algunos lenguajes, las funciones que retornan un valor se declaran con la palabra function, y las subrutinas que no retornan un valor con la palabra sub. En Python no existen las subrutinas. Todas son funciones, todas las funciones devuelven un valor (None si t no devuelves u algo expresamente con la palabra reservada return) y todas las funciones comienzan con la palabra def.

a La funcin tamanyo aproximado() recibe dos parmetros o argumentos, tamanyo o y un kilobyte es 1024 bytes pero ninguno de ellos especica un tipo de datos. En Python, las variables nunca se tipican expl citamente, Python deduce y mantiene el tipo de datos de la variable de forma interna segn el valor que tenga asignado la u misma. En Java y otros lenguajes con tipicacin esttica, debes especicar el o a tipo de datos de los parmetros y valor de retorno de cada funcin. a o En Python nunca especicas el tipo de datos de nada de forma expl cita. Python mantiene el rastro de los tipos de datos de forma interna basndose en los valores que asignes a las variables. a

1.2.1.

Parmetros opcionales y con nombre a

Python permite que los parmetros de una funcin tengan valores por defecto; a o si la funcin se llama (para ejecutarla) si indicar el parmetro Python usar el valor o a a por defecto para asignarlo al parmetro que no se ha especicado en la llamada a a la funcin. Asimismo, los parmetros se pueden pasar en la llamada en cualquier o a orden si se utilizan parmetros con nombre. a Veamos de nuevo la declaracin de la funcin tamanyo aproximado(). o o
1 def tamanyo aproximado ( tamanyo , u n k i l o b y t e e s 1 0 2 4 b y t e s=True ) :

El segundo parmetro un kilobyte es 1024 bytes, especica un valor por defecto a igual a True. Como consecuencia, este parmetro pasa a ser opcional ; puedes llamar a a la funcin sin pasarlo en los parntesis. Python se comportar como si lo hubieras o e a llamado con el valor True como segundo parmetro. a Veamos el nal del script4 :
1 2 3 if name == m a i n : print ( tamanyo aproximado ( 1 0 0 0 0 0 0 0 0 0 0 0 0 , F a l s e ) ) print ( tamanyo aproximado ( 1 0 0 0 0 0 0 0 0 0 0 0 0 ) )
4

En Python se les suele llamar tambin script a los cheros con el cdigo fuente de los programas. e o

1.2. DECLARACION DE FUNCIONES

35

1. La primera llamada a la funcin (l o nea 2) utiliza dos parmetros. Durante la a ejecucin de la funcin tamanyo aproximado un kilobyte es 1024 bytes tendr el o o a valor False, que es lo que se pasa como segundo parmetro en la llamada a la a funcin. o 2. La segunda llamada a la funcin (l o nea 3) utiliza un unico parmetro. Pero a Python no se queja ya que el segundo es opcional. Como no se especica, el segundo parmetro utiliza su valor por defecto True, de acuerdo a lo que se a deni en la declaracin de la funcin. o o o Tambin puedes pasar los valores a una funcin utilizando nombres. Prueba lo e o siguiente en la consola:
1 2 3 4 5 6 7 8 9 10 11 12 >>> from parahumanos import tamanyo aproximado >>> tamanyo aproximado ( 4 0 0 0 , u n k i l o b y t e e s 1 0 2 4 b y t e s=F a l s e ) 4 . 0 KB >>> tamanyo aproximado ( tamanyo =4000 , u n k i l o b y t e e s 1 0 2 4 b y t e s=F a l s e ) 4 . 0 KB >>> tamanyo aproximado ( u n k i l o b y t e e s 1 0 2 4 b y t e s=F a l s e , tamanyo =4000) 4 . 0 KB >>> tamanyo aproximado ( u n k i l o b y t e e s 1 0 2 4 b y t e s=F a l s e , 4 0 0 0 ) SyntaxE rror : nonkeyword a r g a f t e r keyword a r g (< p y s h e l l#4>, l i n e 1) >>> tamanyo aproximado ( tamanyo =4000 , F a l s e ) SyntaxEr ror : nonkeyword a r g a f t e r keyword a r g (< p y s h e l l#5>, l i n e 1) >>>

1. Lnea 2: Llama a la funcin tamnyo aproximado() pasndole 4000 al primer o a parmetro (tamanyo) y el valor False en el denominado un kilobyte es 1204 bytes a (En este caso coincide que el parmetro con nombre se est pasando en la sea a gunda posicin y tambin est declarado en la funcin como segundo parmeo e a o a tro, pero esto es simplemente una coincidencia). a a 2. Lnea 4: Llama a la funcin tamanyo aproximado() pasndole 4000 al parmetro o denominado tamanyo y False al parmetro denominado un kilobyte es 1024 bytes a (Estos parmetros coinciden en orden con los de la declaracin de la funcin, a o o pero vuelve a ser una simple coincidencia). 3. Lnea 6: Llama a a la funcin tamanyo aproximado() pandole False al parme o a a tro denominado un kilobyte es 1024 bytes y 4000 al parmetro denominado taa manyo (Esta es la utilidad de usar nombres en las llamadas a una funcin, o poder pasarlos en cualquier orden, e incluso no pasar alguno de los existentes para que tomen valores por defecto mientras s que pasas uno de los ultimos parmetros de la funcin). a o

36

CAP ITULO 1. TU PRIMER PROGRAMA EN PYTHON 4. Lnea 8: Esta llamada a la funcin falla porque se usa un parmetro con o a nombre seguido de uno sin nombre (por posicin). Esta forma de llamar a la o funcin siempre falla. Python lee la lista de parmetros de izquierda a derecha, o a en cuanto aparece un parmetro con nombre, el resto de parmetros debe a a tambin proporcionarse por nombre. Los primeros pueden ser por posicin. e o 5. Lnea 10: Esta llamada tambin falla por la misma razn que la anterior. Te e o sorprende? Despus de todo, el primer parmetro se ha denominado tamanyo y e a recibe el valor 4000, es obvio que el valor False deber asignarse al parmetro a a un kilobyte es 1024 bytes. Pero Python no funciona de esa forma. Tan pronto como lee un parmetro con nombre, todos los parmetros siguientes (a la a a derecha) tienen que llevar el nombre del parmetro. a

1.3.

Cmo escribir cdigo legible o o

No te voy a aburrir con una larga charla sobre la importancia de documentar el cdigo. Solamente decir que el cdigo se escribe una vez pero se lee muchas veces, o o y que quien ms lo va a leer eres t, seis meses despus de haberlo escrito (por a u e ejemplo: cuando ya no te acuerdes de nada pero necesites corregir o aadir algo). n Python hace fcil escribir cdigo legible, aprovchate de ello. Me lo agradecers a o e a dentro de seis meses.

1.3.1.

Cadenas de texto de documentacin o

Puedes documentar una funcin proporcionndole una cadena de documentao a cin (abreviando se suele hablar de docstring). En este programa, la funcin tamano o o yo aproximado() tiene una cadena de documentacin (docstring):
1 2 3 4 5 6 7 8 9 10 11 12 def tamanyo aproximado ( tamanyo , u n k i l o b y t e e s 1 0 2 4 b y t e s=True ) : C o n v i e r t e un tama o de f i c h e r o en formato l e g i b l e por p e r s o n a s n Argumentos / par metros : a tamanyo tama o de f i c h e r o en b y t e s n u n k i l o b y t e e s 1 0 2 4 b y t e s s i True ( por d e f e c t o ) , usa m l t i p l o s de 1024 u s i F a l s e , usa m l t i p l o s de 1000 u retorna : string

La comillas triples sirven para escribir cadenas de texto que ocupen ms de a

1.4. EL CAMINO DE BUSQUEDA PARA IMPORT

37

una l nea. Todo lo que se escribe entre las comillas triples forma parte de una unica cadena de texto, inclu dos los espacios en blanco, retornos de carro, saltos de l nea y otras comillas sueltas. Este tipo de cadenas de texto lo puedes utilizar donde quieras dentro del cdigo Python, pero normalmente se utilizan para denir docstring o (cadenas de texto de documentacin). o Las comillas triples son la manera ms simple de escribir cadenas de a texto que incluyan, a su vez, comillas simples y/o dobles, como cuando en Perl 5 se utiliza q/.../ En este ejemplo, todo lo que se encuentra entre las comillas triples es el docstring de la funTodas las funciones se merecin, que sirve para documentar lo que hace la o cen un docstring que las explifuncin. Un docstring, si existe, debe ser lo prio que mero que aparece denido en una funcin (es o decir, se debe encontrar en la primera l nea que aparece despus de la declaracin de la funcin). Tcnicamente no necesitas escribir e o o e un docstring para cada funcin, pero deber S que lo has escuchado en las clases o as. e que programacin a las que hayas asistido, pero Python te da un incentivo mayor o para que lo hagas: los docstring estn disponibles en tiempo de ejecucin como un a o atributo de la funcin. o Muchos entornos integrados de programacin (IDEs) utilizan los docstring o para proporcionar ayuda y documentacin sensible al contexto, de forma o que cuando teclees el nombre de una funcin, aparece su docstring como o pista sobre el signicado de la funcin y de sus parmetros. Esto puede o a ser muy util, tan util como explicativos sean los docstring que escribas.

1.4.

El camino de b squeda para import u

Antes de continuar, quiero mencionar brevemente el camino5 de bsqueda de u las librer Cuando importas un mdulo, Python busca en varios lugares hasta enas. o contrarlo. En concreto, busca en todos los directorios que se encuentren denidos en la variable sys.path. Como se trata de una lista, puedes verla fcilmente o modicarla a con los mtodos estndares de manipulacin de listas. (Aprenders a trabajar con e a o a listas en el cap tulo ?? sobre Tipos de Dato Nativos).
En espaol se usa tambin ruta de bsqueda. En ingls se usa la palabra path para referirse a n e u e este concepto
5

38
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

CAP ITULO 1. TU PRIMER PROGRAMA EN PYTHON

>>> import s y s >>> s y s . path [, / u s r / l i b / python3 . 0 , / u s r / l i b / python3 . 0 / p l a t l i n u x 2 , / u s r / l i b / python3 . 0 / l i b dynload , / u s r / l i b / python3 . 0 / d i s t p a c k a g e s , / u s r / l o c a l / l i b / python3 . 0 / d i s t p a c k a g e s ] >>> s y s <module s y s ( b u i l t in)> >>> s y s . path . i n s e r t ( 0 , /home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / e j e m p l o s ) >>> s y s . path [ /home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / e j e m p l o s , , / u s r / l i b / python3 . 0 , / u s r / l i b / python3 . 0 / p l a t l i n u x 2 , / u s r / l i b / python3 . 0 / l i b dynload , / u s r / l i b / python3 . 0 / d i s t p a c k a g e s , / u s r / l o c a l / l i b / python3 . 0 / d i s t p a c k a g e s ] >>>

1. Lnea 1: Al importar el paquete sys de esta forma, todas sus funciones y atributos quedan a disposicin del programador para su uso. o 2. Lneas 2-8: sys.path es una variable (path) del paquete sys que contiene una lista de los directorios que constituyen el camino de bsqueda (El tuyo ser diu a ferente, ya que depende del sistema operativo, de la versin de Python que o tengas instalada, y del lugar en el que est instalada). Siempre que se haga un a import en el cdigo, Python buscar en estos directorios (por orden), hasta eno a contrar un chero cuyo nombre coincida con el valor que se usa en la sentencia import cms la extensin .py. a o 3. Lneas 9-10: En realidad te he mentido un poco, la realidad es un poco ms a compleja, no todos los mdulos se almacenan en cheros con extensin .py. o o Algunos de ellos, como el mdulo sys son mdulos internos (built-in); no exiso o ten en cheros, estn construidos internamente en el propio lenguaje. En la a prctica funcionan exactamente igual que los mdulos que estn en cheros, a o a la unica diferencia es que no existe el cdigo fuente, Porque no estn escritos o a en Python! (El mdulo sys est escrito en lenguaje c). o a 4. Lnea 11: En tiempo de ejecucin puedes aadir un nuevo directorio al ca o n mino de bsqueda de Python aadiendo un directorio a la variable sys.path, u n as Python tambin buscar en l cada vez que intentes importar un mdulo. e a e o El efecto de este cambio dura mientras se mantenga en ejecucin Python. Al o

1.5. EN PYTHON TODO ES UN OBJETO

39

nalizar, y volver a entrar en Python, el camino (la variable sys.path) volver a a tener los valores iniciales. 5. Lneas 12-19 : Al ejecutar sys.path.insert(0, path) se nsert un nuevo directorio o en la primera posicin (en Python la primera posicin se numera con el cero) o o de la lista de sys.path. Casi siempre, ser esto lo que quieras hacer. En casos a en los que exista algn conicto de nombres (por ejemplo, si Python tiene su u propia versin de una librer y es de la versin 2, pero quieres utilizar otra o a o que sea de la versin 3), as te aseguras que tus mdulos se encuentran antes o o y ejecutan en lugar de los originales.

1.5.

En Python todo es un Objeto

En caso de te lo hayas perdido, acabo de decir que las funciones de Python tienen atributos, y que esos atributos estn disponibles en tiempo de ejecucin. Una a o funcin, como todo lo dems en Python, es un objeto. o a Abre la consola interactiva de Python y ejecuta lo siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 >>> import parahumanos >>> print ( parahumanos . tamanyo aproximado ( 4 0 9 6 , True ) ) 4 . 0 KiB >>> print ( parahumanos . tamanyo aproximado . d o c ) C o n v i e r t e un tama o de f i c h e r o en un formato l e g i b l e por p e r s o n a s n Argumentos / par metros : a tamanyo tama o de f i c h e r o en b y t e s n u n k i l o b y t e e s 1 0 2 4 b y t e s s i True ( por d e f e c t o ) , usa m l t i p l o s de 1024 u s i F a l s e , usa m l t i p l o s de 1000 u retorna : string

>>>

1. Lnea 1: Importa (carga en memoria) el programa parahumanos como un mdu o lo un trozo de cdigo que puedes utilizar de forma interactiva o desde un o programa Python mayor. Una vez se ha importado el mdulo, puedes utilio zar (referenciar) cualquiera de sus funciones pblicas, clases o atributos. Si u desde un mdulo se desea utilizar la funcionalidad de otro, basta con hacer o exactamente lo mismo que en esta l nea de la consola interactiva.

40

CAP ITULO 1. TU PRIMER PROGRAMA EN PYTHON 2. Lnea 2: Cuando quieres utilizar las funciones que estn denidas en los mdu e o los importados, tienes que aadir el nombre del mdulo. No es posible utilizar n o simplemente tamanyo aproximado, debes utilizar parahumanos.tamanyo aproximado. Si has utilizado Java, esta forma de utilizar una funcin deber sonarte. o a 3. Lnea 4: En este caso, en lugar de llamar a la funcin como podr esperar, o as se consulta uno de los atributos de la funcin, doc . o En Python import es equivalente al require de Perl. Cuando importas (import) un mdulo de Python puedes acceder a todas sus funciones con o la sintaxis mdulo.funcin. En Perl, cuando se requiere (require) un mduo o o lo puedes acceder a todas sus funciones con la sintaxis mdulo::funcin o o

1.5.1.

Qu es un objeto? e

En Python todo es un objeto, y todos los objetos pueden tener atributos y mtodos. Todas las funciones son objetos, y tienen el atributo doc , que retorna e el docstring que se haya denido en el cdigo fuente. El mdullo sys es tambin un o o e objeto que tiene (entre otras cosas) un atributo denominado path. Como se ha dicho: todo lo que se dena en Python es un objeto y puede tener atributos y mtodos. e Sin embargo, no hemos contestado an a la pregunta fundamental: Qu es un u e objeto? Los diferentes lenguajes de programacin denen objeto de diferente forma. o En algunos, signica que todos los objetos deben tener atributos y mtodos; en otros, e signica que todos los objetos pueden tener subclases. En Python la denicin es o ms relajada. Algunos objetos no tienen ni atributos ni mtodos, pero podran. No a e todos los objetos pueden tener subclases. Pero todo es un objeto en el sentido de que pueden asignarse a variables y pasarse como parmetro de una funcin. a o Puede que hayas o en otro contexto de programacin el trmino objeto de do o e primera clase. En Python, las funciones son objetos de primera clase. Puedes pasar una funcin como parmetro de otra funcin. Los mdulos tambin son objetos de o a o o e primera clase. Puedes pasar un mdulo completo como parmetro de una funcin. o a o Las clases son objetos de primera clase, y las instancias de las clases tambin lo son. e Esto es importante, por lo que lo voy a repetir en caso de que se te escapara las primeras veces: en Python, todo es un objeto. Las cadenas son objetos, las listas son objetos. Las funciones son objetos. Las clases son objetos. Las instancias de las clases son objetos. E incluso los mdulos son objetos. o

1.6. INDENTAR CODIGO

41

1.6.

Indentar cdigo o

Las funciones de Python no tienen begin o end, y tampoco existen llaves que marquen donde comienza y acaba el cdigo de una funcin. El unico delimitador es o o el s mbolo de los dos puntos (:) y el propio indentado del cdigo. o
1 2 3 4 5 6 7 8 9 10 11 def tamanyo aproximado ( tamanyo , u n k i l o b y t e e s 1 0 2 4 b y t e s=True ) : i f tamanyo < 0 : r a i s e V a l u e E r r o r ( El n mero debe s e r no n e g a t i v o ) u m u l t i p l o = 1024 i f u n k i l o b y t e e s 1 0 2 4 b y t e s e l s e 1000 f o r s u f i j o in SUFIJO [ m u l t i p l o ] : tamanyo /= m u l t i p l o i f tamanyo < m u l t i p l o : return { 0 : . 1 f } {1} . format ( tamanyo , s u f i j o ) r a i s e V a l u e E r r o r ( n mero demasiado grande ) u

1. Lnea 1: Los bloques de cdigo se denen por su indentado. Por bloque de o cdigo se entiende lo siguiente: funciones, sentencias if, bucles for, bucles while o y similar. Al indentar se inicia el bloque y al desindentar se naliza. No existen llaves, corchetes o palabras clave para iniciar y nalizar un bloque de forma expl cita. Esto implica que los espacios en blanco son signicativos, y deben ser consistentes. En este ejemplo, el cdigo de la funcin est indentado con o o a cuatro espacios. No es necesario que sean cuatro, pero s que sea consistente y siempre sean los mismos. La primera l nea que no est indentada deliminta el e nal de la funcin. o 2. Lnea 2: En Python, la sentencia if debe contener un bloque de cdigo. Si o 6 la expresin que sigue al if es verdadera se ejecuta el bloque indentado que o contiene el if, en caso contrario lo que se ejecuta es el bloque contenido en el else (si existe). Observa la ausencia de parntesis alrededor de la expresin. e o 3. Lnea 3: Esta l nea se encuentra dentro del bloque de cdigo del if. La sentencia o raise elevar una excepcin (del tipo ValueError, pero unicamente si tamanyo a o 0. 4. Lnea 4: Esta l nea no marca el nal de la funcin. Las l o neas que estan com pletamente en blanco no cuentan. Unicamente sirven para hacer ms legible a el cdigo, pero no cuentan como delimitadores de cdigo. La funcin contina o o o u en la l nea siguiente.
6

Si el resultado de evaluarla es True.

42

CAP ITULO 1. TU PRIMER PROGRAMA EN PYTHON 5. Lnea 6: El bucle for tambin marca el comienzo de un bloque de cdigo. Los e o bloques pueden contener mltiples l u neas, siempre que estn indentadas con e el mismo nmero de espacios. Este bucle for contiene tres l u neas de cdigo en o l. No existe ninguna otra sintxis especial para los bloques de varias l e a neas. Basta con indentar y... seguir adelante con el trabajo!

Despus de algunas protestas iniciales e ine sidiosas analog con Fortran, seguro que hars as a las paces con esta forma de marcar los bloques de cdigo y comenzars a apreciar sus benecios. o a Uno de los mayores benecios es que todos los programas Python tienen un formato similar, al ser la indentacin un requisito del lenguaje y no o un elemento de estilo. La consecuencia inmediata es que los programas Python son ms fciles a a de leer y comprender por parte de una persona diferente de su programador7 .

Python utiliza los saltos de l nea para separar las sentencias y los dos puntos y la indentacin para separar los bloo ques de cdigo. C++ y Java o utilizan puntos y coma para separar sentencias y llaves para separar los bloques de cdio go.

1.7.

Excepciones

Las excepciones estn en todas partes en Python. Prcticamente todos los a a mdulos de la librer estndar las utilizan, y el propio lenguaje las lanza en muchas o a a circunstancias. Las vers una y otra vez a lo largo del libro. a Qu es una excepcin? Normalmente es un error, una indicacin de que algo e o o fue mal (No todas las excepciones son errores, pero no te preocupes por eso ahora). Algunos lenguajes de programacin fomentan que se retornen cdigos de error en o o las funciones, que los programadores tendrn que chequear. Python fomenta el uso a de las excepciones, que los programadores tienen que capturar y manejar. Cuando sucede un error se muestra en la consola de Python algunos detalles de la excepcin y cmo se produjo. A esto se le llama exo o cepcin sin capturar. Cuando la excepcin se geo o ner, Python no encontr un trozo de cdigo que o o o estuviera previsto que la capturase y respondiera en consecuencia, por eso la excepcin se fue eleo vando hasta llegar al nivel ms alto en la consola, a la cual muestra alguna informacin util para la o
7

Al contrario que Java, las funciones de Python no declaran las excepciones que podr an elevar. Te corresponde a ti determinar las excepciones que pueden suceder y necesitas capturar.

o por el propio programador despus de unos meses! e

1.7. EXCEPCIONES

43

depuracin del cdigo y naliza. Si esto sucede o o en la consola no es excesivamente preocupante, pero si le sucede a tu programa en plena ejecucin, el programa nalizar de forma incontrolada y no se capturase la o a excepcin. Puede que sea lo que quieras, pero puede que no. o El hecho de que suceda una excepcin no o implica necesariamente que el programa tenga Python utiliza bloques que fallar. Las excepciones se pueden manejar. try...except para manejar exAlgunas veces una excepcion sucede porque tiecepciones, y la sentencia raise nes un error en el cdigo (como por ejemplo, aco para generarlas. Java y C++ ceder al valor de una variable que no existe), peutilizan bloques try...catch ro en otras ocasiones puedes anticiparlo. Si vas para manejarlas y la sentencia a abrir un chero, puede que no exista. Si vas throw para generarlas. a importar un mdulo, puede que no est instao e lado. Si te vas a conectar a una base de datos, puede que no est disponible, o puede que no tengas las credenciales necesarias para e acceder a ella. Si sabes que una l nea de cdigo puede elevar una excepcin, deber o o as manejar la excepcin utilizando el bloque try...except. o n La funcin tamanyo aproximado() eleva excepciones por dos causas: el tamao o que se pasa es mayor que el previsto, o si es menor que cero.
1 2 i f tamanyo < 0 : r a i s e V a l u e E r r o r ( El n mero debe s e r no n e g a t i v o ) u

La sintaxis para elevar una excepcin es muy simple. Utiliza la sentencia raise, o seguida del nombre de la excepcin y, opcionalmente, se le pasa como parmetro o a una cadena de texto que sirve para propsitos de depuracin. La sintaxis es parecida o o a la de llamar a una funcin 8 . o No necesitas manejar las excepciones en la funcin que las eleva. Si no se o manejan en la funcin que las eleva, las excepciones pasan a la funcin o o que la llam, luego a la que llam a esa, y as sucesivamente a travs de o o e toda la pila de llamadas. Si una excepcin no se manejase en ninguna o funcin, el programa fallar y nalizar, Python imprimir una traza del o a a a error, y punto. Puede que fuese lo que quer o no, depende de lo que as pretendieras, que para eso eres el programador!
Las excepciones son objetos, como todo en Python recuerdas?. Para implementarlas se utilizan clases (class) de objetos. Al ejecutar en este caso la sentencia raise, en realidad se est creando una a instancia de la clase ValueError y pasndole la cadena El nmero debe ser no negativo al mtodo a u e de inicializacin. Pero nos estamos adelantando! o
8

44

CAP ITULO 1. TU PRIMER PROGRAMA EN PYTHON

1.7.1.

Capturar errores al importar

Una de las excepciones internas de Python es ImportError, que se eleva cuando intentas importar un mdulo y falla. Esto puede suceder por diversas causas, pero o la ms simple es que el mdulo no exista en tu camino de bsqueda. Puedes utilizar a o u esta excepcin para incluir caracter o sticas opcionales a tu programa. Por ejemplo, la librer chardet que aparece en el cap a tulo ?? autodetecta la codicacin de caraco teres. Posiblemente tu programa quiera utilizar esta librer si est instalada, pero a a continuar funcionando si no lo est. Para ello puedes utilizar un bloque try...except. a
1 2 3 4 try : import c h a r d e t except I m p o r t E r r o r : c h a r d e t = None

Posteriormente, en el cdigo, puedes consultar la presencia de la librer con o a una simple sentencia if:
1 2 3 4 i f chardet : # hacer algo else : # s e g u i r de t o d o s modos

Otro uso habitual de la excepcion ImportError es cuando dos mdulos impleo 9 mentan una API comn, pero existe preferencia por uno de ellos por alguna causa u (tal vez sea ms rpida, o use menos memoria). Puedes probar a importar un mdua a o lo y si falla cargar el otro. Por ejemplo, en el cap tulo 12 sobre XML se habla de dos mdulos que implementan una API comn, denominada ElementTree API. El o u primero lxml.etree, es un mdulo desarrollado por terceros que requiere descargarlo o e instalarlo t mismo. El segundo, xml.etree.ElementTree, es ms lento pero forma u a parte de la librer estndar de Python 3. a a
1 2 3 4 try : from lxml import e t r e e except I m p o r t E r r o r : import xml . e t r e e . ElementTree a s e t r e e

Al nal de este bloque try...except, has importando algn modulo y lo has u llamado etree. Puesto que ambos mdulos implementan una API comn, el resto del o u cdigo no se tiene que preocupar de qu mdulo se ha cargado10 . Asimismo, como o e o el mdulo que se haya importado termina llamndose etree, el resto del cdigo no o a o
Application Programming Interface. Interfaz de programacin de aplicaciones. o Nota del Traductor:Al implementar la misma API ambos mdulos se comportan igual por lo o que son indistingubles en cuanto a funcionamiento. As el resto del cdigo puede funcionar sin , o conocer qu mdulo se ha importado realmente. e o
10 9

1.8. VARIABLES SIN DECLARAR

45

tiene que estar repleto de sentencias if para llamar a diferentes mdulos con diferente o nombre.

1.8.

Variables sin declarar

Echale otro vistazo a la siguiente l nea de cdigo de la funcin tamanyo aproximado: o o


1 m u l t i p l o = 1024 i f u n k i l o b y t e e s 1 0 2 4 b y t e s e l s e 1000

La variable multiplo no se ha declarado en ningn sitio, simplemente se le u asigna un valor. En Python es correcto. Lo que no te dejar hacer nunca Python a es referenciar a una variable a la que nunca le has asignado un valor. Si intentas hacerlo se elevar la excepcin NameError. a o
1 >>> x 2 Traceback ( most r e c e n t c a l l l a s t ) : 3 F i l e <s t d i n > , l i n e 1 , in <module> 4 NameError : name x i s not d e f i n e d 5 >>> x = 1 6 >>> x 7 1

Le dars las gracias frecuentemente a Python por avisarte! a

1.9.

May sculas y min sculas u u

En Python, todos los nombres distinguen maysculas y minsculas: los nomu u bres de variables, de funciones, de mdulos, de excepciones. De forma que no es el o mismo nombre si cambia alguna letra de mayscula a minscula o viceversa. u u

46
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

CAP ITULO 1. TU PRIMER PROGRAMA EN PYTHON

>>> u n e n t e r o = 1 >>> u n e n t e r o 1 >>> UN ENTERO Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> NameError : name UN ENTERO i s not d e f i n e d >>> Un Entero Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> NameError : name Un Entero i s not d e f i n e d >>> un enteRo Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> NameError : name un enteRo i s not d e f i n e d

Y as siempre si pruebas todas las combinaciones posibles.

1.10.

Ejecucin de scripts o

Los mdulos de Python son objetos, por lo o que tienen propiedades muy utiles. Puedes utili Todo lo que existe en Python zar alguna de ellas para probar tus mdulos de o es un Objeto una forma sencilla. Para ello puedes incluir un bloque de cdigo especial que se ejecute cuando o arrancas el chero desde la l nea de comando. Observa las ultimas l neas de parahumanos.py:
1 2 3 if name == m a i n : print ( tamanyo aproximado ( 1 0 0 0 0 0 0 0 0 0 0 0 0 , F a l s e ) ) print ( tamanyo aproximado ( 1 0 0 0 0 0 0 0 0 0 0 0 0 ) )

Como en C, Python utiliza == para las comparaciones y = para las asignaciones. Al contrario que C, Python no permite la asignacin en o l nea, por lo que no es posible asignar un valor por accidente cuando tu intencin fuese comparar. o Qu es lo que hace este if tan especial? Como los mdulos son objetos, tienen e o propiedades, y una de las propiedades de los mdulos es name . El valor de la o propiedad name depende de la forma en la que ests utilizando el mdulo. Si e o importas el mdulo con la sentencia import el valor que contiene name es el o nombre del chero del mdulo sin la extensin. o o

1.11. LECTURAS COMPLEMENTARIAS


1 >>> import parahumanos 2 >>> parahumanos . n a m e 3 parahumanos

47

Pero tambin puedes ejecutar directamente el mdulo como un programa e o autnomo, en cuyo caso name contiene el valor especial main . En el ejemplo, o Python evaluar la sentencia if, la expresin ser verdadera y ejecutar el bloque de a o a a cdigo contenido en el if. En este caso, imprimir dos valores: o
1 2 3 j m g a g u i l e r a @ a c e r N e t b o o k : / inmersionEnPython3 / s r c $ python3 parahumanos . py 1 . 0 TB 9 3 1 . 3 GiB

Y as queda explicado tu primer programa Python!

1.11.

Lecturas complementarias

PEP 257: Docstring Conventions Convenciones para escribir docstring. Explica lo que distingue un buen docstring de un gran docstring. Tutorial de Python: Cadenas de texto para documentacin tambin aborda la o e materia. PEP 8: Gu de estilo para codicacin en Python comenta cual es el estilo a o recomendado de indentacin. o Manual de referencia de Python explica lo que signica decir que todo en Python es un objeto, porque algunas personas son algo pedantes y les gusta discutir largo y tendido sobre ese tipo de cosas.

48

CAP ITULO 1. TU PRIMER PROGRAMA EN PYTHON

Cap tulo 2 Tipos de dato nativos


Nivel de dicultad:    La curiosidad es la base de toda la losof a, las preguntas alimentan su progreso, la ignorancia su n. Michel de Montaigne

2.1.

Inmersin o

Aparta tu primer programa en Python durante unos minutos, y vamos a hablar sobre tipos de dato. En Python cada valor que exista, tiene un tipo de dato, pero no es necesario declarar el tipo de las variables. Como funciona? Basado en cada asignacin a la variable, Python deduce el tipo que es y lo conserva internamente. o Python proporciona muchos tipos de dato nativos. A continuacin se muestran o los ms importantes: a 1. Booleanos: Su valor es True o False. 2. N meros: Pueden ser enteros (1, 2, 3,...), otantes (1.1, c 1.3,...)1 , fracciou 1.2, nes (1/2, 1/3, 2/3,...), o incluso nmeros complejos (i  1). u 3. Cadenas: Son secuencias de caracteres Unicode, por ejemplo, un documento HTML.
Nota del traductor: los nmeros decimales se representan utilizando punto decimal. Aunque en u espaol utilizamos la coma decimal en este libro usamos el punto decimal por ser el formato que n se requiere en Python.
1

49

50

CAP ITULO 2. TIPOS DE DATO NATIVOS 4. Bytes y arrays de bytes: por ejemplo, un chero de imgenes JPEG. a 5. Listas: Son secuencias ordenadas de valores. 6. Tuplas: Son secuencias ordenadas e inmutables de valores. 7. Conjuntos: Son bolsas de valores sin ordenar. 8. Diccionarios: Son bolsas de sin ordenar de parejas clave-valor. Es posible buscar directamente por clave.

Aparte de estos, hay bastantes ms tipos. Todo es un objeto en Python, por a lo que existen tipos module, function, class, method, le, e incluso compiled code 2 . Ya has visto alguno de ellos en el cap tulo anterior. En el cap tulo ?? aprenders las a clases, y en el cap tulo 11 los cheros (tambin llamados archivos). e Las cadenas y bytes son sucientemente importantes y complejas como para merecer un cap tulo aparte. Vamos a ver los otros tipos de dato en primer lugar.

2.2.

Booleanos

El tipo de datos booleano solamente tiene dos valores posibles: verdadero o fals. Python En la prctica, puedes utilizar a dispone de dos constantes denominadas True y casi cualquier expresin en un o False, que se pueden utilizar para asignar valores contexto booleano. booleanos directamente. Las expresiones tambin se pueden evaluar a un valor booleano. En e algunos lugares (como las sentencias if, Python espera una expresin que se pueda o evaluar a un valor booleano. Estos sitios se denominan contextos booleanos. Puedes utilizar casi cualquier expresin en un contexto booleano, Python intentar determio a nar si el resultado puede ser verdadero o falso. Cada tipo de datos tiene sus propias reglas para identicar qu valores equivalen a verdadero y falso en un contexto booe leano (Esto comenzar a tener un sentido ms claro para ti cuando veas algunos a a ejemplos concretos). Por ejemplo:
1 2
2

i f tamanyo < 0 : r a i s e V a l u e E r r o r ( e l n mero debe s e r no n e g a t i v o ) u Nota del traductor: Son tipos de dato del lenguaje Python que representan a: mdulos, funo ciones, clases, mtodos, cheros y cdigo compilado. e o

2.3. NUMEROS

51

La variable tamanyo contiene un valor entero, 0 es un entero, y es un operador numrico. El resultado de la expresin es siempre un valor de tipo booleano. Puedes e o comprobarlo en la consola interactiva de Python:
1 2 3 4 5 6 7 8 9 >>> tamanyo >>> tamanyo False >>> tamanyo >>> tamanyo False >>> tamanyo >>> tamanyo True = 1 < 0 = 0 < 0 = 1 < 0

Debido a la herencia que se conserva de Python 2, los booleanos se pueden tratar como si fuesen nmeros. True es 1 y False es 0. u
1 2 3 4 5 6 7 8 9 10 >>> True + True 2 >>> True F a l s e 1 >>> True F a l s e 0 >>> True / F a l s e Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> Z e r o D i v i s i o n E r r o r : i n t d i v i s i o n or modulo by z e r o

An as no hagas esto. Olvida incluso que lo he mencionado!3 u ,

2.3.

N meros u

Los nmeros son maravillosos. Tienes un montn donde elegir. Python prou o porciona enteros y nmeros de coma otante, pero no existen declaraciones de tipo u para distinguirlos. Python los distingue por la existencia o no del punto decimal4 .
Nota del traductor: se trata de una prctica heredada de Python 2 pero que no puede consia derarse buena prctica de programacin. a o 4 Nota del traductor: En espaol se dice coma decimal, como vamos a mostrar puntos decimales n en todo el libro, por coherencia se utilizar tambin el trmino punto decimal. a e e
3

52
1 2 3 4 5 6 7 8 9 10 >>> type ( 1 ) <c l a s s i n t > >>> i s i n s t a n c e ( 1 , i n t ) True >>> 1 + 1 2 >>> 1 + 1 . 0 2.0 >>> type ( 2 . 0 ) <c l a s s f l o a t >

CAP ITULO 2. TIPOS DE DATO NATIVOS

1. Lnea 1: la funcin type() permite consultar el tipo de cualquier valor o varia o ble. Como era de esperar 1 es un valor de tipo int. 2. Lnea 3: la funcin isinstance() permite chequear si un valor o variable es de o un tipo determinado. 3. Lnea 5: La suma de dos valores de tipo int da como resultado otro valor de tipo int. 4. Lnea 7: La suma de un valor int con otro de tipo oat da como resultado un valor de tipo oat. Python transforma el valor entero en un valor de tipo oat antes de hacer la suma. El valor que se devuelve es de tipo oat.

2.3.1.

Convertir enteros en otantes y viceversa

Como acabas de ver, algunos operadores (como la suma) convierten los nmeu ros enteros en otantes si es necesario. Tambin puedes convertirlos t mismo. e u
1 2 3 4 5 6 7 8 9 10 11 12 >>> f l o a t ( 2 ) 2.0 >>> i n t ( 2 . 0 ) 2 >>> i n t ( 2 . 5 ) 2 >>> i n t ( 2.5) 2 >>> 1 . 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1.1234567890123457 >>> type ( 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) <c l a s s i n t >

1. Lnea 1: Utilizando la funcin oat() puedes convertir expl o citamente un valor de tipo int en oat.

2.3. NUMEROS

53

2. Lnea 3: Como era de prever, la conversin inversa se hace utilizando la funcin o o int(). 3. Lnea 5: La funcin int() trunca el valor otante, no lo redondea. o 4. Lnea 7: La funcin int() trunca los valores negativos hacia el 0. Es una ver o dadera funcin de truncado, no es una funcin de suelo5 . o o 5. Lnea 9: En Python, la precisin de los nmeros de punto otante alcanza 15 o u posiciones decimales. 6. Lnea 11: En Python, la longitud de los nmeros enteros no est limitada. u a Pueden tener tantos d gitos como se requieran. Python 2 ten dos tipos separados int y long. El tipo int estaba limitaa do por el sistema sys.maxint, siendo diferente segn la plataforma, pero u usualmente era 232 1. Python 3 tiene un unico tipo entero que, en su mayor parte, equivale al tipo long de Python 2. Para conocer ms detalles a consulta PEP 237.

2.3.2.

Operaciones numricas habituales e

Puedes hacer muchos tipos de clculos con nmeros. a u


1 2 3 4 5 6 7 8 9 10 11 12 >>> 5.5 >>> 5 >>> 6 >>> 5.0 >>> 121 >>> 1 11 / 2 11 // 2

11

// 2

1 1 . 0 // 2 11 2 11 % 2

1. Lnea 1: El operador / efecta una divisin en punto otante. El resultado u o siempre es de tipo oat, incluso aunque ambos operadores (dividendo y divisor) sean int.
Nota del traductor: en ingls oor function, que redondea siempre al entero menor, por lo e que el nmero -2.5 ser convertido a -3 en el caso de aplicarle una funcin de suelo u a o
5

54

CAP ITULO 2. TIPOS DE DATO NATIVOS 2. Lnea 3: El operador // efecta una divisin entera algo extraa. Cuando el u o n resultado es positivo, el resultado es int truncado sin decimales (no redondeado). 3. Lnea 5: Cuando el operador // se usa para dividir un nmero negativo el u resultado se redondea hacia abajo al entero ms prximo (en este caso el a o resultado de la divisin ser -5.5, que redondeado es -5). o a 4. Lnea 7: El operador ** signica elevado a la potencia de. 112 es 121. 5. Lnea 9: El operador % devuelve el resto de la divisin entera. 11 dividido o entre 2 es 5 con un resto de 1, por lo que el resultado en este caso es 1. En Python 2, el operador / se usaba para representar a la divisin entera, o aunque mediante una directiva de Python 2, pod hacer que se comas portase como una divisin de punto otante. En Python 3, el operador o / siempre es una divisin de punto otante. Para consultar ms detalles o a puedes mirar PEP 238.

2.3.3.

Fracciones

Python no est limitado a nmeros enteros y de punto otante. Tambin puede a u e aplicar toda esa matemtica que aprendiste en el instituto y que luego rpidamente a a olvidaste.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 >>> import f r a c t i o n s >>> x = f r a c t i o n s . F r a c t i o n ( 1 , 3 ) >>> x Fraction (1 , 3) >>> x 2 Fraction (2 , 3) >>> f r a c t i o n s . F r a c t i o n ( 6 , 4 ) Fraction (3 , 2) >>> f r a c t i o n s . F r a c t i o n ( 0 , 0 ) Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> F i l e f r a c t i o n s . py , l i n e 9 6 , in new r a i s e Z e r o D i v i s i o n E r r o r ( F r a c t i o n( %s , 0 ) % numerator ) ZeroDivisionError : Fraction (0 , 0)

1. Lnea 1: Para comenzar a utilizar fracciones hay que importar el mdulo frac o tions.

2.3. NUMEROS

55

2. Lnea 2: Para denir una fraccin crea un objeto Fraction y psale el numerador o a y denominador. 3. Lnea 5: Con las fracciones puedes efectuar los clculos matemticos habitua a a les. Estas operaciones devuelven un objeto Fraction, 2*(1/2)=(2/3). 4. Lnea 7: El objeto Fraction reduce automticamente las fracciones: (6/4) = a (3/2). 5. Lnea 9: Python tiene el buen sentido de no crear una fraccin con el denomi o nador a cero.

2.3.4.

Trigonometr a

Tambin puedes hacer clculos trigonomtricos en Python. e a e


1 2 3 4 5 6 7 >>> import math >>> math . p i 3.1415926535897931 >>> math . s i n ( math . p i / 2 ) 1.0 >>> math . tan ( math . p i / 4 ) 0.99999999999999989

1. Lnea 2: El mdulo math tiene denida una constante que almacena el valor del o nmero , la razn de la circunferencia de un c u o rculo respecto de su dimetro. a 2. Lnea 4: En el mdulo math se encuentran todas las funciones trigonomtricas o e bsicas, incluidas sin(), cos(), tan() y variantes como asin(). a 3. Lnea 6: De todos modos ten en cuenta que Python no tiene precisin innita, o tan(/4) deber devolver 1.0, no 0.99999999999999989. a

2.3.5.

N meros en un contexto booleano u

El valor cero es equivalente a falso y los valores distintos de cero son equivalentes a verdadero. Los nmeros se pueden utilizar en contextos booleanos, como en la sentencia u if. El valor cero es equivalente a falso y los valores distintos de cero son equivalentes a verdadero.

56
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 >>> ... ... ... ... ... >>> s , >>> s , >>> no , >>> s , >>> no , >>> >>> s , >>> no , def e s t r u e ( a n y t h i n g ) : i f anything : print ( s , e s t r u e ) else : print ( no , e s f a l s e )

CAP ITULO 2. TIPOS DE DATO NATIVOS

es true (1) es true e s t r u e ( 1) es true es true (0) es f a l s e es true (0.1) es true es true (0.0) es f a l s e import f r a c t i o n s es true ( f r a c t i o n s . Fraction (1 , 2)) es true es true ( f r a c t i o n s . Fraction (0 , 1)) es f a l s e

1. Lnea 1: Sab que puedes denir tus propias funciones en la consola inter as activa de Python? Simplemente pulsa INTRO al nal de cada l nea, y termina pulsando un ultimo INTRO en una l nea en planco para nalizar la denicin o de la funcin. o 2. Lnea 7: En un contexto booleano, como el de la sentencia if, los nmeros u enteros distintos de cero se evalan a True; el nmero cero se evala a False. u u u 3. Lnea 13: Los nmeros en punto otante distintos de cero son True; 0.0 se u evala a False. Ten cuidado con este caso! Al ms m u a nimo fallo de redondeo (que no es imposible, como has visto en el apartado anterior) Python se encontrar comprobando el nmero 0.0000000000001 en lugar del 0 y retornar a u a True. 4. L nea 18: Las fracciones tambin se pueden utilizar en un contexto booleano. e Fraction(0, n) se evala a False para cualquier valor de n. Todas las otras u fracciones se evalan a True. u

2.4. LISTAS

57

2.4.

Listas

El tipo de datos List es el ms utilizado en Python. Cuando digo lista, puede a que pienses en un array6 , cuyo tamao he declarado anteriormente a su uso, que n unicamente puede contener elementos del mismo tipo. No pienses eso, las listas son mucho ms guays. a Una lista de Python es como un array de Perl 5. En Perl 5 las variables que almacenan arrays siempre comienzan con el carcter @. En Python a las variables se pueden nombrar como se quiera, ya que Python mantiene el tipo de datos internamente. Una lista de Python es mucho ms que un array de Java (aunque puede a utilizarse como si lo fuese si eso es lo que quieres). Una analog mejor a ser pensar en la clase ArrayList de Java, que puede almacenar un nmero a u arbitrario de objetos y expandir su tamao dinmicamente al aadir n a n nuevos elementos.

2.4.1.

Crear una lista

Crear una lista es fcil: utiliza unos corchetes para para delimitar una lista de a valores separados por coma.
1 2 3 4 5 6 7 8 9 10 11 >>> l i s t a = [ a , b , j m g a g u i l e r a , z , e j e m p l o ] >>> l i s t a [ a , b , jmgaguilera , z , ejemplo ] >>> l i s t a [ 0 ] a >>> l i s t a [ 4 ] ejemplo >>> l i s t a [ 1] ejemplo >>> l i s t a [ 3] jmgaguilera

1. Lneas 1 a 3: Primero denimos una lista de cinco elementos. Observa que mantiene el orden original. No es por casualidad. Una lista es un conjunto ordenado de elementos.
6

matriz de una o ms dimensiones a

58

CAP ITULO 2. TIPOS DE DATO NATIVOS 2. Lnea 4: Se puede acceder a los elementos de la lista como en el caso de los arrays de Java, teniendo en cuenta que el primer elemento se numera como cero. El primer elemento de cualquier lista no vac es lista[0]. a 3. Lnea 6: El ultimo elemento de esta lista de cinco elementos es lista[4], puesto que los elementos se indexan contando desde cero. 4. Lnea 8: Si se usan ndices con valor negativo se accede a los elementos de la lista contando desde el nal. El ultimo elemento de una lista siempre se puede indexar utilizando lista[-1]. 5. Lnea 10: Si los nmeros negativos en los u ndices te resultan confusos, puedes pensar de esta forma: lista[-n] == lista[len(lista)-n]. Por eso, en esta lista, lista[3] == lista[5 - 3] == lista[2].

2.4.2.

Particin de listas o

lista[0] es el primer elemento de la lista. Una vez has denido una lista, puedes obtener cualquier parte de ella como una nueva lista. A esto se le llama particionado 7 de la lista.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 >>> [ a >>> [ b >>> [ b >>> [ a >>> [ a >>> [ z >>> [ a lista , b , jmgaguilera , z , ejemplo ] lista [1:3] , jmgaguilera ] l i s t a [1: 1] , jmgaguilera , z ] lista [0:3] , b , jmgaguilera ] lista [:3] , b , jmgaguilera ] lista [3:] , ejemplo ] lista [:] , b , jmgaguilera , z , ejemplo ]

1. Lnea 3: Puedes obtener una parte de una lista especicando dos ndices. El valor de retorno es una nueva lista que contiene los elementos de la lista original, en orden, comenzando en el elemento que estaba en la posicin del o primer ndice (en este caso lista[1]), hasta el elemento anterior al indicado por el segundo ndice (en este caso lista[3]).
7

En ingls: slicing. e

2.4. LISTAS

59

2. Lnea 5: El particionado de listas tambin funciona si uno o ambos e ndices son negativos. Si te sirve de ayuda puedes imaginrtelo as leyendo la lista de a : izquierda a derecha, el primer ndice siempre especica el primer elemento que quieres obtener y el segundo ndice el primer elemento que no quieres obtener. El valor de retorno es una lista con todos los elementos que estn entre ambos a ndices. 3. Lnea 7: Los ndices de las listas comienzan a contar en cero, por eso un particionado de lista[0:3] devuelve los primeros tres elementos de la lista, comenzando en lista[0], pero sin incluir lista[3]. 4. Lnea 9: Si el primer ndice es cero, puedes omitirlo. Python lo deducir. Por a eso lista[:3] es lo mismo que lista[0:3]. 5. Lnea 11: De igual forma, si el segundo ndice es la longitud de la cadena, puedes omitirlo. Por eso, en este caso, lista[3:] es lo mismo que lista[3:5], al tener esta lista cinco elementos. Existe una elegante simetr aqu En esta a . lista de cinco elementos lista[:3] devuelve los 3 primeros elementos y lista[3:] devuelve una lista con los restantes. De hecho, lista[:n] siempre retornar los n a primeros elementos y lista[n:] los restantes, sea cual sea el tamao de la lista. n 6. Lnea 13: Si se omiten ambos ndices, se obtiene una nueva lista con todos los elementos de la lista original. Es una forma rpida de hacer una copia de una a lista.

2.4.3.

A adir elementos a una lista n

Existen cuatro maneras de aadir elementos a una lista. n


1 2 3 4 5 6 7 8 9 10 11 12 13 >>> l i s t a = [ a ] >>> l i s t a = l i s t a + [ 2 . 0 , 3 ] >>> l i s t a [ a , 2.0 , 3] >>> l i s t a . append ( True ) >>> l i s t a [ a , 2 . 0 , 3 , True ] >>> l i s t a . extend ( [ c u a t r o , omega ] ) >>> l i s t a [ a , 2 . 0 , 3 , True , c u a t r o , omega ] >>> l i s t a . i n s e r t ( 0 , omega ) >>> l i s t a [ omega , a , 2 . 0 , 3 , True , c u a t r o , omega ]

1. Lnea 2: El operador + crea una nueva lista a partir de la concatenacin de o otras dos. Una lista puede contener cualquier nmero de elementos, no hay u

60

CAP ITULO 2. TIPOS DE DATO NATIVOS l mite de tamao (salvo el que imponga la memoria disponible). Si embargo, n si la memoria es importante, debes tener en cuenta que la concatenacin de o listas crea una tercera lista en memoria. En este caso, la nueva lista se asigna inmediatamente a la variable lista. Por eso, esta l nea de cdigo se efecta en o u dos pasos concatenacin y luego asignacin que puede (temporalmente) o o consumir mucha memoria cuando las listas son largas. 2. Lnea 3: Una lista puede contener elementos de cualquier tipo, y cada elemento puede ser de un tipo diferente. Aqu tenemos una lista que contiene una cadena de texto, un nmero en punto otante y un nmero entero. u u 3. Lnea 5: El mtodo append() aade un nuevo elemento, unico, al nal de la e n lista (Ahora ya tenemos cuatro tipos de dato diferentes en la lista!). 4. Lnea 8: Las listas son clases. Crear una lista realmente consiste en instanciar una clase. Por eso las listas tienen mtodos que sirven para operar con ellas. e El mtodo extend() toma un parmetro, una lista, y aade cada uno de sus e a n elementos a la lista original. 5. L nea 11: El mtodo insert() inserta un unico elemento a la lista original. El e primer parmetro es el a ndice del primer elemento de la lista original que se desplazar de su posicin para aadir los nuevos. Los elementos no tienen que a o n ser unicos; por ejemplo, ahora hay dos elementos separados en la lista cuyo valor es omega: el primer elemento lista[0] y el ultimo elemento lista[6]. La llamada al mtodo lista.insert(0,valor) es equivalente a la funcin unse o hift() de Perl. Aade un elemento al comienzo de la lista, y los restantes n elementos se desplazan para hacer sitio. Vamos a ver ms de cerca la diferencia entre append() y extend(). a

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

>>> l i s t a = [ a , b >>> l i s t a . extend ( [ d >>> l i s t a [ a , b , c , d , >>> l e n ( l i s t a ) 6 >>> l i s t a [ 1] f >>> l i s t a . append ( [ g >>> l i s t a [ a , b , c , d , >>> l e n ( l i s t a ) 7 >>> l i s t a [ 1] [ g , h , i ]

, c ] , e , f ]) e , f ]

, h , i ] ) e , f , [ g , h , i ] ]

2.4. LISTAS

61

1. Lnea 2: El mtodo extend() recibe un unico parmetro, que siempre es una e a lista, y aade cada uno de sus elementos al nal de la lista original lista. n 2. Lnea 5: Si una lista de tres elementos se extiende con una lista de otros tres elementos, la lista resultante tiene seis elementos. 3. Lnea 9: Por otra parte, el mtodo append() recibe un unico parmetro, que e a puede ser de cualquier tipo. En este caso estamos ejecutando el mtodo pasndoe a le una lista de tres elementos. 4. Lnea 12: Si partes de una lista de seis elementos y aades otra lista a ella, n nalizas con una lista de... siete elementos. Porqu siete? Porque el ultimo e elemento (que acabas de aadir) es en s mismo una lista. Una lista puede n contener datos de cualquier tipo, inclu das otras listas. Puede que sea lo que quieras o puede que no. Pero es lo que le has pedido a Python al ejecutar append() con una lista como parmetro. a

2.4.4.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

B squeda de valores en una lista u

>>> l i s t a = [ a , b , nuevo , mpilgrim , nuevo ] >>> l i s t a . count ( nuevo ) 2 >>> nuevo in l i s t a True >>> c in l i s t a False >>> l i s t a . i n d e x ( mpilgrim ) 3 >>> l i s t a . i n d e x ( nuevo ) 2 >>> l i s t a . i n d e x ( c ) Traceback ( i n n e r m o s t l a s t ) : F i l e < i n t e r a c t i v e input > , l i n e 1 , in ? V a l u e E r r o r : l i s t . i n d e x ( x ) : x not in l i s t

1. Lnea 2: Como te puedes imaginar, el mtodo count() devuelve el nmero de e u veces que aparece un valor espec co el parmetro en la lista. a 2. Lnea 4: Si lo unico que quieres saber es si un valor se encuentra o no en la lista, el operador in es ligeramente ms rpido que el mtodo count(). El operador a a e in devuelve True o False, no indica en qu lugar de la lista se encuentra el e elemento, ni el nmero de veces que aparece. u

62

CAP ITULO 2. TIPOS DE DATO NATIVOS 3. Lnea 8: Si necesitas conocer el lugar exacto en el que se encuentra un valor dentro de la lista debes utilizar el mtodo index(). Por defecto, este mtodo e e buscar en toda la lista, aunque es posible especicar un segundo parmetro a a para indicar el lugar de comienzo (0 es el primer ndice), e incluso, un tercer elemento para indicar el ndice en el que parar la bsqueda. u 4. Lnea 10: El mtodo index() encuentra la primera ocurrencia del valor en la e lista. En este caso el valor nuevo aparece dos veces, en la posicin 2 y en la o 4, el mtodo devuelve la posicin de la primera ocurrencia: 2. e o 5. Lnea 12: Puede que no esperases, pero el mtodo index() eleva una excepcin e o ValueError cuando no es capaz de encontrar el elemento en la lista.

Espera un momento! Qu signica eso? Pues lo que he dicho: el mtodo e e index() eleva una excepcin si no es capaz de encontrar el valor en la lista. Esto o es diferente de la mayor de los lenguajes de programacin que suelen devolver a o algn u ndice no vlido, como por ejemplo, -1. Aunque al principio te pueda parecer a algo desconcertante, creo que con el tiempo llegars a apreciarlo. Signica que tu a programa fallar en la fuente del problema, en lugar de fallar ms tarde en algn otro a a u lugar por no haber contemplado la posibilidad de que un elemento no se encontrara en la lista. Recuerda que -1 es un valor de ndice vlido. Si el mtodo index() devolviera a e -1... las sesiones de depuracin ser bastante complicadas! o an

2.4.5.

Eliminar elementos de una lista

Las listas nunca tienen huecos Las listas se expanden y contraen de forma automtica. Ya has visto como a expandirlas. Existen varios modos de eliminar elementos de una lista.
1 2 3 4 5 6 7 8 >>> l i s t a = [ a , b , nuevo , mpilgrim , nuevo ] >>> l i s t a [ 1 ] b >>> del l i s t a [ 1 ] >>> l i s t a [ a , nuevo , mpilgrim , nuevo ] >>> l i s t a [ 1 ] nuevo

1. Lnea 4: Para eliminar un elemento de una lista puedes utilizar la sentencia del.

2.4. LISTAS

63

2. Lnea 7: Si intentas acceder al elemento en la posicin 1 despus de borrar o e el ndice 1 no da error. Despus de borrar un elemento, todos los elementos e que iban detrs de l se desplazan a la izquierda para rellenar el vac que a e o dej el elemento eliminado. o Y si no conoces la posicin del elemento? No hay problema, en vez de la o posicin, puedes utilizar el valor del elemento para eliminarlo. o
1 2 3 4 5 6 7 8 9 10 >>> l i s t a . remove ( nuevo ) >>> l i s t a [ a , mpilgrim , nuevo ] >>> l i s t a . remove ( nuevo ) >>> l i s t a [ a , mpilgrim ] >>> l i s t a . remove ( nuevo ) Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> V a l u e E r r o r : l i s t . remove ( x ) : x not in l i s t

1. Lnea 1: Puedes eliminar un elemento de una lista utilizando el mtodo re e move(). Este mtodo recibe como parmetro un valor y elimina la primera e a ocurrencia de ese valor en la lista. Como antes, todos los elementos a la derecha del eliminado, se desplazan a la izquierda para rellenar el vac puesto o, que las listas nunca tienen huecos. 2. Lnea 4: Puedes llamar al mtodo remove() tantas veces como sea necesario. e Pero si se intenta eliminar un valor que no se encuentre en la lista, el mtodo e elevar una excepcin ValueError. a o

2.4.6.

Eliminar elementos de una lista: ronda extra

Otro mtodo de inters que tiene las listas es pop(), que permite eliminar e e elementos de una lista de un modo especial.

64
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

CAP ITULO 2. TIPOS DE DATO NATIVOS

>>> l i s t a = [ a , b , nuevo , mpilgrim ] >>> l i s t a . pop ( ) mpilgrim >>> l i s t a [ a , b , nuevo ] >>> l i s t a . pop ( 1 ) b >>> l i s t a [ a , nuevo ] >>> l i s t a . pop ( ) nuevo >>> l i s t a . pop ( ) a >>> l i s t a . pop ( ) Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> I n d e x E r r o r : pop from empty l i s t

1. Lnea 2: Cuando se llama sin parmetros, el mtodo pop() elimina el ultimo a e valor de la lista y devuelve el valor eliminado. 2. Lnea 6: Es posible extraer cualquier elemento de una lista. Para ello hay que pasar el ndice deseado al mtodo pop(). Se eliminar el elemento indicado, los e a siguientes se movern a la izquierda rellenar el vac y se devuelve el valor a o recin eliminado. e 3. Lnea 14: Si llamas al mtodo pop() con una lista vac se eleva una excepcin. e a o El mtodo pop() sin argumentos se comporta igual que la funcin pop() e o de Perl. Elimina el ultimo valor de la lista y lo devuelve. Perl dispone de otra funcin, shift(), que eliminar el primer elemento y devuelve su o valor; en Python es equivalente a lista.pop(0).

2.4.7.

Listas en contextos booleanos

Las listas vac equivalen a falso, todas las dems a verdadero. as a Puedes utilizar las listas en contextos booleanos, como en la sentencia if.

2.5. TUPLAS
1 2 3 4 5 6 7 8 9 10 11 12 >>> ... ... ... ... ... >>> no , >>> s , >>> s , def e s t r u e ( a n y t h i n g ) : i f anything : print ( s , e s t r u e ) else : print ( no , e s f a l s e ) es es es es es es true ( [ ] ) false true ([ a ]) true true ( [ False ] ) true

65

1. Lnea 7: En un contexto booleano una lista vac vale False. a 2. Lnea 9: Cualquier lista con al menos un elemento vale True. 3. Lnea 11: Cualquier lista con al menos un elemento vale True. El valor de los elementos de la lista es irrelevante.

2.5.

Tuplas

Una tupla es una lista inmutable. Una tupla no se puede modicar despus de e haberla creado.
1 2 3 4 5 6 7 8 9 >>> t u p l a = ( a , b , mpilgrim , z , e j e m p l o ) >>> t u p l a ( a , b , mpilgrim , z , e j e m p l o ) >>> t u p l a [ 0 ] a >>> t u p l a [ 1] ejemplo >>> t u p l a [ 1 : 3 ] ( b , mpilgrim )

1. Lnea 1: Las tuplas se denen de la misma forma que las listas. La unica diferencia es que los elementos se cierran entre parntesis en lugar de corchetes. e 2. Lnea 4: Los elementos de una tupla estn ordenados como los de una lista. a Los ndices tambin comienzan a contar en cero, por lo que el primer elemento e de una tupla siempre es tupla[0]. 3. Lnea 6: Los ndices negativos cuentan desde el nal de la tupla como en las listas.

66

CAP ITULO 2. TIPOS DE DATO NATIVOS 4. Lnea 8: El particionado tambin funciona como en las listas. Una particin e o de una tupla es una nueva tupla con los elementos seleccionados.

Lo que diferencia a las tuplas de las listas es que las primeras no se pueden modicar. En trminos tcnicos se dice que son inmutables. En trminos prcticos e e e a esto signica que no tienen mtodos que te permitan modicarlas. Las listas tienen e mtodos como append(), extend(), insert(), remove() y pop(). Las tuplas no tienen e ninguno de estos mtodos. Puedes particionar una tupla porque en realidad se crea e una nueva tupla, y puedes consultar si contienen un valor determinado, y... eso es todo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # c o n t i n u a c i n d e l e j e m p l o a n t e r i o r o >>> t u p l a ( a , b , mpilgrim , z , e j e m p l o ) >>> t u p l a . append ( nuevo ) Traceback ( i n n e r m o s t l a s t ) : F i l e < i n t e r a c t i v e input > , l i n e 1 , in ? A t t r i b u t e E r r o r : t u p l a o b j e c t has no a t t r i b u t e append >>> t u p l a . remove ( z ) Traceback ( i n n e r m o s t l a s t ) : F i l e < i n t e r a c t i v e input > , l i n e 1 , in ? A t t r i b u t e E r r o r : t u p l a o b j e c t has no a t t r i b u t e remove >>> t u p l a . i n d e x ( e j e m p l o ) 4 >>> z in t u p l a True

1. Lnea 4: No puedes aadir elementos a una tupla. No existen los mtodos n e append() o extend(). 2. Lnea 8: No puedes eliminar elementos de una tupla. No existen los mtodos e remove() o pop(). 3. Lnea 12: S puedes buscar elementos en una tupla puesto que consultar no cambia la tupla. 4. Lnea 14: Tambin puedes utilizar el operador in para chequear si existe un e elemento en la tupla. Para qu valen las tuplas? e Las tuplas son ms rpidas que las listas. Si lo que denes es un conjunto a a esttico de valores y todo lo que vas a hacer es iterar a travs de ellos, lo mejor a e es que uses una tupla en lugar de una lista.

2.5. TUPLAS

67

Es ms seguro, puesto que proteges contra escritura los datos que no necesitas a modicar. Algunas tuplas se pueden utilizar como claves de diccionarios como veremos ms adelante en el cap a tulo. Las listas nunca se pueden utilizar como claves de diccionarios. Las tuplas se pueden convertir en listas y viceversa. La funcin interna o tuple() puede recibir como parmetro una lista y devuelve una tupla con a los mismos elementos que tenga la lista, y la funcin list() toma como o parmetro una tupla y retorna una lista. En la prctica la funcin tuple() a a o congela una lista, y la funcin list() descongela una tupla. o

2.5.1.

Tuplas en un contexto booleano

Las tuplas tambin se pueden utilizar en un contexto booleano: e


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 >>> def e s t r u e ( a n y t h i n g ) : ... i f anything : ... print ( s , e s t r u e ) ... else : ... print ( no , e s f a l s e ) ... >>> e s t r u e ( ( ) ) no , e s f a l s e >>> e s t r u e ( ( a , b ) ) s , e s t r u e >>> e s t r u e ( ( F a l s e , ) ) s , e s t r u e >>> type ( ( F a l s e ) ) <c l a s s b o o l > >>> type ( ( F a l s e , ) ) <c l a s s t u p l e >

1. Lnea 7: Una tupla vac siempre vale false en un contexto booleano. a 2. Lnea 9: Una tupla con al menos un valor vale true. 3. Lnea 11: Una tupla con al menos un valor vale true. El valor de los elementos es irrelevante. Pero qu hace esa coma ah e ? 4. Lnea 13: Para crear una tupla con un unico elemento, necesitas poner una coma despus del valor. Sin la coma Python asume que lo que ests haciendo e a es poner un par de parntesis a una expresin, por lo que no se crea una tupla. e o

68

CAP ITULO 2. TIPOS DE DATO NATIVOS

2.5.2.

Asignar varios valores a la vez

A continuacin se observa una forma muy interesante de programar mltiples o u asignaciones en Python. Para ello utilizamos las tuplas:
1 2 3 4 5 6 7 8 >>> v = ( a , 2 , True ) >>> ( x , y , z ) = v >>> x a >>> y 2 >>> z True

1. Lnea 2: v es una tupla con tres elementos y (x, y, z) es una tupla con tres variables. Al asignar una tupla a la otra, lo que sucede es que cada una de las variables recoge el valor del elemento de la otra tupla que corresponde con su posicin. o Esto tiene toda clase de usos. Supn que quieres asignar nombres a un rango de o valores, puedes combinar la funcin range() con la asignacin mltiple para hacerlo o o u de una forma rpida: a
1 2 3 4 5 6 7 8 >>> ... >>> 0 >>> 1 >>> 6 (LUNES, MARTES, MIERCOLES, JUEVES, VIERNES, SABADO, DOMINGO) = r a n g e ( 7 ) LUNES MARTES DOMINGO

1. Lnea 1: La funcin interna range() genera una secuencia de nmeros enteros8 . o u Las variables que vas a denir son LUNES, MARTES, etc)9 . El mdulo calendar o dene unas constantes enteras para cada d de la semana). a 2. Lnea 3: Ahora cada variable tiene un valor: LUNES vale 0, MARTES vale 1, etc. Tambin puedes utilizar la asignacin mltiple para construir funciones que e o u devuelvan varios valores a la vez. Simplemente devolviendo una tupla con los valores.
Tcnicamente construye un iterador, no una lista o tupla. Lo veremos ms adelante. e a Este ejemplo procede del mdulo calendar, que es un pequeo mdulo que imprime un caleno n o dario, como el programa de UNIX cal
9 8

2.6. CONJUNTOS

69

Desde el cdigo que llama a la funcin se puede tratar el valor de retorno como una o o tupla o se puede asignar los valores individuales a unas variables. Muchas librer as estndares de Python hacen esto, incluido el mdulo os, que utilizaremos en el a o siguiente cap tulo.

2.6.

Conjuntos

Un conjunto es una bolsa sin ordenar de valores unicos. Un conjunto puede contener simultneamente valores de cualquier tipo de datos. Con dos conjuntos a se pueden efectuar las t picas operaciones de unin, interseccin y diferencia de o o conjuntos.

2.6.1.

Creacin de conjuntos o

Comencemos por el principio, crear un conjunto es fcil. a


1 2 3 4 5 6 7 8 >>> u n c o n j u n t o = {1} >>> u n c o n j u n t o {1} >>> type ( u n c o n j u n t o ) <c l a s s s e t > >>> u n c o n j u n t o = { 1 , 2} >>> u n c o n j u n t o { 1 , 2}

1. Lnea 1: Para crear un conjunto con un valor basta con poner el valor entre llaves (). 2. Lnea 4: Los conjuntos son clases, pero no te preocupes por ahora de esto. 3. Lnea 6: Para crear un conjunto con varios elementos basta con separarlos con comas y encerrarlos entre llaves. Tambin es posible crear un conjunto a partir de una lista: e
1 2 3 4 5 6 >>> u n a l i s t a = [ a , b , mpilgrim , True , F a l s e , 4 2 ] >>> u n c o n j u n t o = s e t ( u n a l i s t a ) >>> u n c o n j u n t o { a , F a l s e , b , True , mpilgrim , 42} >>> u n a l i s t a [ a , b , mpilgrim , True , F a l s e , 4 2 ]

70

CAP ITULO 2. TIPOS DE DATO NATIVOS 1. Lnea 2: Para crear un conjunto de una lista utiliza la funcin set()10 . o 2. Lnea 3: Como coment anteriormente, un conjunto puede contener valores de e cualquier tipo y est desordenado. En este ejemplo, el conjunto no recuerda a el orden en el que estaba la lista que sirvi para crearlo. Si aadieras algn o n u elemento nuevo no recordar el orden en el que lo aadiste. a n 3. Lnea 5: La lista original no se ha modicado.

Tienes un conjunto vac Sin problemas. Puedes crearlo y ms tarde aadir o? a n elementos.
1 2 3 4 5 6 7 8 9 10 >>> u n c o n j u n t o = s e t ( ) >>> u n c o n j u n t o set () >>> type ( u n c o n j u n t o ) <c l a s s s e t > >>> l e n ( u n c o n j u n t o ) 0 >>> n o s e g u r o = {} >>> type ( n o s e g u r o ) <c l a s s d i c t >

1. Lnea 1: Para crear un conjunto vac debes utilizar la funcin set() sin parme o o a tros. 2. Lnea 2: La representacin impresa de un conjunto vac parece algo extraa. o o n Tal vez estabas esperando {}? Esa expresin se utiliza para representar un o diccionario vac no un conjunto vac Aprenders a usar los diccionarios ms o, o. a a adelante en este cap tulo. 3. Lnea 4: A pesar de la extraa representacin impresa se trata de un conjunto. n o 4. Lnea 6: ...y este conjunto no tiene elementos. 5. Lnea 8: Debido a razones histricas procedentes de Python 2. No puedes o utilizar las llaves para crear un conjunto vac puesto que lo que se crea es un o, diccionario vac no un conjunto vac o, o.
Aquellos que conocen cmo estn implementados los conjuntos apuntarn que realmente no se o a a trata de una llamada a una funcin, sino de la instanciacin de una clase. Te prometo que en este o o libro aprenders la diferencia. Pero por ahora basta con que sepas que set() se comporta como una a funcin que devuelve como resultado un conjunto. o
10

2.6. CONJUNTOS

71

2.6.2.

Modicacin de conjuntos o

Hay dos maneras de aadir valores a un conjunto: el mtodo add() y el mtodo n e e update().
1 2 3 4 5 6 7 8 9 10 11 >>> >>> >>> {1 , >>> 3 >>> >>> {1 , >>> 3 u n c o n j u n t o = { 1 , 2} u n c o n j u n t o . add ( 4 ) un conjunto 2 , 4} len ( un conjunto ) u n c o n j u n t o . add ( 1 ) un conjunto 2 , 4} len ( un conjunto )

1. Lnea 2: El mtodo add() recibe un parmetro, que puede ser de cualquier e a tipo, cuyo resultado es aadir el parmetro al conjunto. n a 2. Lnea 5: Este conjunto tiene ahora cuatro elementos. 3. Lnea 7: Los conjuntos son bolsas de valores unicos. Por eso, si intentas aadir un valor que ya exista en el conjunto no har nada. Tampoco elevar un n a a error. Simplemente no se hace nada. 4. Lnea 10: Por eso, el conjunto an tiene tres elementos. u
1 2 3 4 5 6 7 8 9 10 11 12 >>> >>> {1 , >>> >>> {1 , >>> >>> {1 , >>> >>> {1 , un un 2, un un 2, un un 2, un un 2, c o n j u n t o = { 1 , 2 , 3} conjunto 3} c o n j u n t o . update ( { 2 , 4 , 6 } ) conjunto 3 , 4 , 6} c o n j u n t o . update ( { 3 , 6 , 9 } , { 1 , 2 , 3 , 5 , 8 , 1 3 } ) conjunto 3 , 4 , 5 , 6 , 8 , 9 , 13} c o n j u n t o . update ( [ 1 0 , 2 0 , 3 0 ] ) conjunto 3 , 4 , 5 , 6 , 8 , 9 , 1 0 , 1 3 , 2 0 , 30}

1. Lnea 4: El mtodo update() toma un parmetro, un conjunto, y aade todos e a n sus elementos al conjunto original. Funciona como si llamaras al mtodo add() e con cada uno de los elementos del conjunto que pasas como parmetro. a

72

CAP ITULO 2. TIPOS DE DATO NATIVOS 2. Lnea 5: Los elementos duplicados se ignoran puesto que los conjuntos no pueden contener duplicados. 3. Lnea 7: Puedes llamar al mtodo update() con cualquier nmero de parme e u a tros. Cuando lo llamas con dos conjuntos, el mtodo aade todos los elementos e n de cada conjunto al conjunto original (sin incluir duplicados). 4. Lnea 10: El mtodo update() puede recibir como parmetro elementos de e a diferentes tipos de dato, incluidas las listas. Cuando se llama pasndole una a lista, el mtodo update() aade todos los elementos de la lista al conjunto e n original.

2.6.3.

Eliminar elementos de un conjunto

Existen tres formas de eliminar elementos individuales de un conjunto: Las dos primeras discard() y remove(), se diferencian de forma sutil:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 >>> u n c o n j u n t o = { 1 , 3 , 6 , 1 0 , 1 5 , 2 1 , 2 8 , 3 6 , 45} >>> u n c o n j u n t o { 1 , 3 , 3 6 , 6 , 1 0 , 4 5 , 1 5 , 2 1 , 28} >>> u n c o n j u n t o . d i s c a r d ( 1 0 ) >>> u n c o n j u n t o { 1 , 3 , 3 6 , 6 , 4 5 , 1 5 , 2 1 , 28} >>> u n c o n j u n t o . d i s c a r d ( 1 0 ) >>> u n c o n j u n t o { 1 , 3 , 3 6 , 6 , 4 5 , 1 5 , 2 1 , 28} >>> u n c o n j u n t o . remove ( 2 1 ) >>> u n c o n j u n t o { 1 , 3 , 3 6 , 6 , 4 5 , 1 5 , 28} >>> u n c o n j u n t o . remove ( 2 1 ) Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> KeyError : 21

1. Lnea 4: El mtodo discard() toma un unico parmetro y elminia el elemento e a del conjunto. 2. Lnea 7: Si llamas al mtodo discard() con un valor que no exista en el conjunto e no se produce ningn error. Simplemente no se hace nada. u 3. Lnea 10: El mtodo remove() tambin recibe un unico parmetro y tambin e e a e elimina el elemento del conjunto. 4. Lnea 13: Aqu esta la diferencia: si el valor no existe en el conjunto, el mtodo e remove() eleva la excepcin KeyError. o

2.6. CONJUNTOS Como pasa con las listas, los conjuntos tambin tienen el mtodo pop(): e e
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 >>> u n c o n j u n t o = { 1 , 3 , 6 , 1 0 , 1 5 , 2 1 , 2 8 , 3 6 , 45} >>> u n c o n j u n t o . pop ( ) 1 >>> u n c o n j u n t o . pop ( ) 3 >>> u n c o n j u n t o . pop ( ) 36 >>> u n c o n j u n t o { 6 , 1 0 , 4 5 , 1 5 , 2 1 , 28} >>> u n c o n j u n t o . c l e a r ( ) >>> u n c o n j u n t o set () >>> u n c o n j u n t o . pop ( ) Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> KeyError : pop from an empty s e t

73

1. Lnea 2: El mtodo pop() elimina un unico valor del conjunto y retorna el e valor. Sin embargo, como los conjuntos no estn ordenados, no hay un ltimo a u elemento, por lo que no hay forma de controlar qu elemento es el que se extrae. e Es aleatorio. 2. Lnea 10: El mtodo clear() elimina todos los valores del conjunto dejndolo e a vac Es equivalente a un conjunto = set(), que crear un nuevo conjunto o. a vac y lo asignar a la variable, eliminando el conjunto anterior. o a 3. Lnea 13: Si se intenta extraer un valor de un conjunto vac se eleva la ex o cepcin KeyError. o

2.6.4.

Operaciones t picas de conjuntos

Los conjuntos de Python permiten las operaciones habituales de este tipo de datos:

74
1 2 3 4 5 6 7 8 9 10 11 12 13 14

CAP ITULO 2. TIPOS DE DATO NATIVOS

>>> u n c o n j u n t o = { 2 , 4 , 5 , 9 , 1 2 , 2 1 , 3 0 , 5 1 , 7 6 , 1 2 7 , 195} >>> 30 in u n c o n j u n t o True >>> 31 in u n c o n j u n t o False >>> o t r o c o n j u n t o = { 1 , 2 , 3 , 5 , 6 , 8 , 9 , 1 2 , 1 5 , 1 7 , 1 8 , 21} >>> u n c o n j u n t o . union ( o t r o c o n j u n t o ) { 1 , 2 , 1 9 5 , 4 , 5 , 6 , 8 , 1 2 , 7 6 , 1 5 , 1 7 , 1 8 , 3 , 2 1 , 3 0 , 5 1 , 9 , 127} >>> u n c o n j u n t o . i n t e r s e c t i o n ( o t r o c o n j u n t o ) { 9 , 2 , 1 2 , 5 , 21} >>> u n c o n j u n t o . d i f f e r e n c e ( o t r o c o n j u n t o ) { 1 9 5 , 4 , 7 6 , 5 1 , 3 0 , 127} >>> u n c o n j u n t o . s y m m e t r i c d i f f e r e n c e ( o t r o c o n j u n t o ) { 1 , 3 , 4 , 6 , 8 , 7 6 , 1 5 , 1 7 , 1 8 , 1 9 5 , 1 2 7 , 3 0 , 51}

1. Lnea 2: Para comprobar si un valor est contenido en un conjunto se puede a utilizar el operador in. Funciona igual que en las listas. 2. Lnea 7: El mtodo union() retorna un nuevo conjunto que contiene todos los e elementos que estn en alguno de los conjuntos originales. a 3. Lnea 9: El mtodo intersection() retorna un nuevo conjunto con los elementos e que estn en ambos conjuntos originales. a 4. L nea 11: El mtodo dierence() retorna un nuevo conjunto que contiene los e elementos que estn en un conjunto pero no en otro conjunto. a 5. Lnea 13: El mtodo symmetric dierence() retorna un nuevo conjunto que e contiene todos los elementos que estn unicamente en uno de los conjuntos a originales. Tres de estos mtodos son simtricos: e e
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # c o n t i n u a c i n d e l e j e m p l o a n t e r i o r o >>> o t r o c o n j u n t o . s y m m e t r i c d i f f e r e n c e ( u n c o n j u n t o ) { 3 , 1 , 1 9 5 , 4 , 6 , 8 , 7 6 , 1 5 , 1 7 , 1 8 , 5 1 , 3 0 , 127} >>> o t r o c o n j u n t o . s y m m e t r i c d i f f e r e n c e ( u n c o n j u n t o ) == \ . . . un conjunto . symmetric difference ( otro conjunto ) True >>> o t r o c o n j u n t o . union ( u n c o n j u n t o ) == \ . . . u n c o n j u n t o . union ( o t r o c o n j u n t o ) True >>> o t r o c o n j u n t o . i n t e r s e c t i o n ( u n c o n j u n t o ) == \ . . . un conjunto . i n t e r s e c t i o n ( otro conjunto ) True >>> o t r o c o n j u n t o . d i f f e r e n c e ( u n c o n j u n t o ) == \ . . . un conjunto . d i f f e r e n c e ( otro conjunto ) False

2.6. CONJUNTOS

75

1. Lnea 2: Aunque el resultado de la diferencia simtrica de un conjunto y e otro conjunto parezca diferente de la diferencia simtrica de otro conjunto y e a un conjunto, recuerda que los conjuntos estn desordenados. Dos conjuntos con los mismos valores se consideran iguales. 2. Lnea 4: Y eso es lo que sucede aqu No te despistes por la representacin . o impresa de los conjuntos. Como contienen los mismos valores, son iguales. 3. Lnea 7: La unin de dos conjuntos tambin es simtrica. o e e 4. Lnea 10: La interseccin de dos conjuntos tambin es simtrica. o e e 5. Lnea 13: La diferencia de dos conjuntos no es simtrica, lo que tiene sentido, e es anlogo a la resta de dos nmeros; importa el orden de los operandos. a u Finalmente veamos algunas consultas que se pueden hacer a los conjuntos:
1 2 3 4 5 6 7 8 9 10 11 >>> u n c o n j u n t o = { 1 , 2 , 3} >>> o t r o c o n j u n t o = { 1 , 2 , 3 , 4} >>> u n c o n j u n t o . i s s u b s e t ( o t r o c o n j u n t o ) True >>> o t r o c o n j u n t o . i s s u p e r s e t ( u n c o n j u n t o ) True >>> u n c o n j u n t o . add ( 5 ) >>> u n c o n j u n t o . i s s u b s e t ( o t r o c o n j u n t o ) False >>> o t r o c o n j u n t o . i s s u p e r s e t ( u n c o n j u n t o ) False

1. Lnea 3: un conjunto es un subconjunto de otro conjunto Todos los miembros de un conjunto forman parte de otro conjunto. 2. Lnea 5: Pregunta lo mismo pero al revs. otro conjunto es un supercon e junto de un conjunto Todos los miembros de un conjunto forman parte de otro conjunto. 3. Lnea 7: Tan pronto como aadas un valor a un conjunto que no se encuentre n en otro conjunto ambas consultas devuelven False.

2.6.5.

Los conjuntos en contextos booleanos

Puedes utilizar conjuntos en contextos booleanos, como en una sentencia if.

76
1 2 3 4 5 6 7 8 9 10 11 12 >>> ... ... ... ... ... >>> no , >>> s , >>> s , def e s t r u e ( a l g o ) : i f algo : print ( s , e s t r u e ) else : print ( no , e s f a l s e ) es es es es es es true ( set ()) false t r u e ({ a }) true t r u e ({ False }) true

CAP ITULO 2. TIPOS DE DATO NATIVOS

1. Lnea 7: En un contexto booleano los conjuntos vac valen False. os 2. Lnea 9: Cualquier conjunto con al menos un elemento vale True. 3. Lnea 11: Cualquier conjunto con al menos un elemento vale True. El valor de los elementos es irrelevante.

2.7.

Diccionarios

Un diccionario es un conjunto desordenado de parejas clave-valor. Cuando aades una clave a un diccionario, tienes que aadir tambin un valor para esa n n e clave11 . Los diccionarios de Python estn optimizados para recuperar fcilmente el a a 12 valor cuando conoces la clave, no al revs . e Un diccionario de Python, es como un hash de Perl 5. En Perl 5 las variables que almacenan hashes siempre comienzan por el carcter %. a En Python, las variables pueden tener el nombre que se quiera porque el tipo de datos se mantiene internamente.

2.7.1.

Creacin de diccionarios o

Crear diccionarios es sencillo. La sintaxis es similar a la de los conjuntos, pero en lugar de valores, tienes que poner parejas clave-valor. Una vez has creado el diccionario, puedes buscar los valores mediante el uso de su clave.
Ms tarde puedes cambiar el valor asignado a la clave si lo deseas. a Nota del Traductor: en otros lenguajes se habla de arrays asociativos o tablas hash para representar este mismo concepto
12 11

2.7. DICCIONARIOS
1 2 3 4 5 6 7 8 9 10 11

77

>>> u n d i c = { s e r v i d o r : db . d i v e i n t o p y t h o n 3 . o r g , b a s e d a t o s : mysql } >>> u n d i c { s e r v i d o r : db . d i v e i n t o p y t h o n 3 . o r g , b a s e d a t o s : mysql } >>> u n d i c [ s e r v i d o r ] db . d i v e i n t o p y t h o n 3 . o r g >>> u n d i c [ b a s e d a t o s ] mysql >>> u n d i c [ db . d i v e i n t o p y t h o n 3 . o r g ] Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> KeyError : db . d i v e i n t o p y t h o n 3 . o r g

1. Lnea 1: En el ejemplo creamos primero un diccionario con dos elementos y lo asignamos a la variable un dic. Cada elemento es una pareja clave-valor. El conjunto completo de elementos se encierra entre llaves. 2. Lnea 4: servidor es una clave, y su valor asociado se obtiene mediante la referencia un dic[servidor] cuyo valor es db.diveintopython3.org. 3. Lnea 6: basedatos es una clave y su valor asociado se obtiene mediante la referencia un dic[basedatos] cuyo valor es mysql. 4. Lnea 8: Puedes recuperar los valores mediante la clave, pero no puedes re cuperar las claves mediante el uso de su valor. Por eso un dic[servidor] vale db.diveintopython3.org pero un dic[db.diveintopython3.org] eleva una excepcin de tipo KeyError al no ser una clave del diccionario. o

2.7.2.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

Modicacin de un diccionario o

>>> u n d i c { s e r v i d o r : db . d i v e i n t o p y t h o n 3 . o r g , b a s e d a t o s : mysql } >>> u n d i c [ b a s e d a t o s ] = b l o g >>> u n d i c { s e r v i d o r : db . d i v e i n t o p y t h o n 3 . o r g , b a s e d a t o s : b l o g } >>> u n d i c [ u s u a r i o ] = mark >>> u n d i c { s e r v i d o r : db . d i v e i n t o p y t h o n 3 . o r g , u s u a r i o : mark , basedatos : blog } >>> u n d i c [ u s u a r i o ] = dora >>> u n d i c { s e r v i d o r : db . d i v e i n t o p y t h o n 3 . o r g , u s u a r i o : dora , basedatos : blog } >>> u n d i c [ U s u a r i o ] = mark >>> u n d i c { U s u a r i o : mark , s e r v i d o r : db . d i v e i n t o p y t h o n 3 . o r g , u s u a r i o : dora , b a s e d a t o s : b l o g }

78

CAP ITULO 2. TIPOS DE DATO NATIVOS 1. Lnea 3: No puedes tener claves duplicadas en un diccionario. Al asignar un valor a una clave existente el valor anterior se pierde. 2. L nea 6: Puedes aadir nuevas parejas clave-valor en cualquier momento. La n sintaxis es idntica a la que se utiliza para modicar valores. e 3. Lnea 8: El elemento nuevo del diccionario (clave usuario, valor mark) apa rece en la mitad. Esto es una mera coincidencia, los elementos de un diccionario no estn ordenados. a 4. Lnea 10: Al asignar un valor a una clave existente, simplemente se sustituye el valor anterior por el nuevo. 5. Lnea 14: Esta sentencia cambia el valor de la clave usuario para volver le asignar mark? No! Si lo observas atentamente vers que la U est en a a maysculas. Las claves de los diccionarios distinguen las maysculas y minscuu u u las, por eso esta sentencia crea una nueva pareja clave-valor, no sobreescribe la anterior. Puede parecerte casi lo mismo, pero en lo que a Python respecta, es totalmente diferente.

2.7.3.

Diccionarios con valores mixtos

Los diccionarios no se usan unicamente con cadenas de texto. Los valores de un diccionario pueden ser de cualquier tipo, incluidos enteros, booleanos, cualquier objeto o incluso otros diccionarios. Y en un mismo diccionario, no es necesario que todos los valores sean del mismo tipo, puedes mezclarlos segn lo necesites. Los tipos u de datos que pueden ser claves de un diccionario estn ms limitados, pero pueden a a ser cadenas de texto, enteros, y algunos tipos ms. Tambin es factible mezclar a e diferentes tipos de clave en un mismo diccionario. De hecho, ya hemos visto un diccionario con valores diferentes a cadenas de texto.
1 2 SUFIJOS = { 1 0 0 0 : [ KB , MB , GB , TB , PB , EB , ZB , YB ] , 1 0 2 4 : [ KiB , MiB , GiB , TiB , PiB , EiB , ZiB , YiB ] }

Vamos a descomponerlo en la consola interactiva de Python.

2.7. DICCIONARIOS
1 2 3 4 5 6 7 8 9 10 11 12

79

>>> SUFIJOS = { 1 0 0 0 : [ KB , MB , GB , TB , PB , EB , ZB , YB ] , ... 1 0 2 4 : [ KiB , MiB , GiB , TiB , PiB , EiB , ZiB , YiB ] } >>> l e n ( SUFIJOS ) 2 >>> 1000 in SUFIJOS True >>> SUFIJOS [ 1 0 0 0 ] [ KB , MB , GB , TB , PB , EB , ZB , YB ] >>> SUFIJOS [ 1 0 2 4 ] [ KiB , MiB , GiB , TiB , PiB , EiB , ZiB , YiB ] >>> SUFIJOS [ 1 0 0 0 ] [ 3 ] TB

1. Lnea 3: Como sucede con las listas y conjuntos, la funcin len() devuelve el o nmero de claves que tiene un diccionario. u 2. Lnea 5: Tambin como pasa con las listas y conjuntos puedes utilizar el ope e rador in para comprobar si una clave determinada est en el diccionario. a 3. Lnea 7: 1000 es una clave del diccionario SUFIJOS; su valor es una lista de ocho elementos (ocho cadenas de texto, por ser ms precisos). a 4. Lnea 9: De igual manera, 1024 es una clave del diccionario SUFIJOS; su valor tambin es una lista de ocho elmentos. e 5. Lnea 11: Puesto que SUFIJOS[1000] es una lista, puedes utilizar los corchetes para acceder a los elementos individuales. Recuerda que los ndices en Python comienzan a contar en cero.

2.7.4.

Diccionarios en un contexto booleano

Tambin puedes utilizar un diccionario en un contexto booleano, como en la e sentencia if. Todo diccionario vac equivale a False y todos los dems equivalen a True. o a

80
1 >>> def e s t r u e ( a l g o ) : 2 ... i f algo : 3 ... print ( s , e s t r u e ) 4 ... else : 5 ... print ( no , e s f a l s e ) 6 ... 7 >>> e s t r u e ( { } ) 8 no , e s f a l s e 9 >>> e s t r u e ( { a : 1 } ) 10 s , e s t r u e

CAP ITULO 2. TIPOS DE DATO NATIVOS

1. Lnea 7: En un contexto booleano un diccionario vac equivale a False. o 2. Lnea 9: Cualquier diccionario con, al menos, una pareja clave-valor equivale a True.

2.8.

None

None es una constante especial de Python. Representa al valor nulo. None no es lo mismo que False. None tampoco es 0. None tampoco es la cadena vac Cualquier a. comparacin de None con otra cosa diferente de l mismo se evala al valor False. o e u None es el unico valor nulo. Tiene su propio tipo de dato (NoneType). Puedes asignar None a cualquier variable, pero no puedes crear nuevos objetos del tipo NoneType. Todas las variables cuyo valor es None son iguales entre s .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 >>> type ( None ) <c l a s s NoneType > >>> None == F a l s e False >>> None == 0 False >>> None == False >>> None == None True >>> x = None >>> x == None True >>> y = None >>> x == y True

2.9. LECTURAS COMPLEMENTARIAS

81

2.8.1.

None en un contexto booleano

En un contexto booleano None vale False y not None vale True.


1 2 >>> def e s t r u e ( a l g o ) : 3 ... i f algo : 4 ... print ( s , e s t r u e ) 5 ... else : 6 ... print ( no , e s f a l s e ) 7 ... 8 >>> e s t r u e ( None ) 9 no , e s f a l s e 10 >>> e s t r u e ( { not None ) 11 s , e s t r u e

2.9.

Lecturas complementarias

Operaciones booleanas Tipos numricos e Tipos secuencia Tipos conjunto Tipos mapa mdulo fractions o mdulo math o PEP 237: Unicacin de enteros largos y enteros o PEP 238: Modicacin del operador de divisin o o

82

CAP ITULO 2. TIPOS DE DATO NATIVOS

Cap tulo 3 Comprensiones


Nivel de dicultad:    Nuestra imaginacin est desplegada a ms no poder, no como en la ccin, para o a a o imaginar las cosas que no estn realmente ah a , sino para entender aquellas que s lo estn. a Rychard Feynman

3.1.

Inmersin o

Este cap tulo te explicar las listas por comprensin, diccionarios por coma o prensin y conjuntos por comprensin: tres conceptos centrados alrededor de una o o tcnica muy potente. Pero antes vamos a dar un pequeo paseo alrededor de dos e n mdulos que te van a servir para navegar por tu sistema de cheros. o

3.2.

Trabajar con cheros y directorios

Python 3 posee un mdulo denominado os que es la contraccin de operating o o system1 . El mdulo os contiene un gran nmero de funciones para recuperar o u y en algunos casos, modicar informacin sobre directorios, cheros, procesos o y variables del entorno local. Python hace un gran esfuerzo por ofrecer una API unicada en todos los sistemas operativos que soporta, por lo que tus programas pueden funcionar en casi cualquier ordenador con el m nimo de cdigo espec o co posible.
1

Sistema Operativo.

83

84

CAP ITULO 3. COMPRENSIONES

3.2.1.

El directorio de trabajo actual

Cuando te inicias en Python, pasas mucho tiempo en la consola interactiva. A lo largo del libro vers muchos ejemplos que siguen el siguiente patrn: a o 1. Se importa uno de los mdulos de la carpeta de ejemplos. o 2. Se llama a una funcin del mdulo. o o 3. Se explica el resultado. Si no sabes cul es el directorio actual de trabajo, el primer paso probablemente a elevar la excepcin ImportError. Porqu? Porque Python buscar el mdulo en a o e a o el camino de bsqueda actual (ver cap u tulo ??), pero no lo encontrar porque la a carpeta ejemplos no est incluida en l. Para superar este problema hay dos soluciones a e posibles: 1. Aadir el directorio de ejemplos al camino de bsqueda de importacin. n u o 2. Cambiar el directorio de trabajo actual a la carpeta de ejemplos. El directorio de trabajo actual es una propiedad invisible que Python mantiene en memoria. Siempre existe un directorio de trabajo actual: en la consola interactiva de Python; durante la ejecucin de un programa desde la l o nea de comando, o durante la ejecucin de un programa Python como un CGI de algn servidor web. o u El mdulo os contiene dos funciones que te permiten gestionar el directorio de o trabajo actual.
1 2 3 4 5 6 >>> import o s >>> print ( o s . getcwd ( ) ) /home/ j m g a g u i l e r a >>> o s . c h d i r ( /home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / e j e m p l o s ) >>> print ( o s . getcwd ( ) ) /home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / e j e m p l o s

1. Lnea 1: El mdulo os viene instalado con Python. Puedes importarlo siempre o que lo necesites. 2. Lnea 2: Utiliza la funcin os.getcwd() para recuperar el directorio de trabajo o actual. Cuando ejecutas la consola interactiva, Python toma como directorio de trabajo actual aqul en el que te encontrases en el sistema operativo antes e de entrar en la consola; si ejecutas la consola desde una opcin de men del o u

3.2. TRABAJAR CON FICHEROS Y DIRECTORIOS

85

sistema operativo, el directorio de trabajo ser aqul en el que se encuentre el a e programa ejecutable de Python o tu directorio de trabajo por defecto2 . 3. Lnea 4: Utiliza la funcin os.chdir() para cambiar de directorio. Conviene o utilizar la convencin de escribir los separadores en el estilo de Linux (con las o barras inclinadas adelantadas) puesto que este sistema es universal y funciona tambin en Windows. Este es uno de los lugares en los que Python intenta e ocultar las diferencias entre sistemas operativos.

3.2.2.

Trabajar con nombres de cheros y directorios

Aprovechando que estamos viendo los directorios, quiero presentarte el mdulo o os.path, que contiene funciones para manipular nombres de cheros y directorios.
1 2 3 4 5 6 7 8 9 10 11 12 13 >>> import o s >>> print ( o s . path . j o i n ( /home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / e j e m p l o s / , parahumanos . py ) ) /home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / e j e m p l o s / parahumanos . py >>> print ( o s . path . j o i n ( /home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / e j e m p l o s , parahumanos . py ) ) /home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / e j e m p l o s / parahumanos . py >>> print ( o s . path . expanduser ( ) ) /home/ j m g a g u i l e r a >>> print ( o s . path . j o i n ( o s . path . expanduser ( ) , i n m e r s i o n e n p y t h o n 3 , examples , humansize . py ) ) /home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / e j e m p l o s \ parahumanos . py

1. Lnea 2: La funcin os.path.join() construye un nombre completo de chero o o directorio (nombre de path) a partir de uno o ms partes. En este caso a unicamente tiene que concatenar las cadenas. 2. Lnea 5: Este caso es menos trivial. La funcin aade una barra inclinada antes o n de concatenar. Dependiendo de que el ejemplo se construya en Windows o en una versin de Linux o Unix, la barra inclinada ser invertida o no. Python o a ser capaz de encontrar el chero o directorio independientemente del sentido a en el que aparezcan las barras inclinadas. En este caso, como el ejemplo lo constru en Linux, la barra inclinada es la t pica de Linux. 3. Lnea 8: La funcin os.path.expanduser() obtendr un camino completo al di o a rectorio que se exprese y que incluye como indicador el directorio ra del z
2

Esto depende del sistema operativo: windows, linux, ...

86

CAP ITULO 3. COMPRENSIONES usuario conectado. Esto funcionar en todos los sistemas operativos que tena gan el concepto de directorio ra del usuario, lo que incluye OS X, Linux, z Unix y Windows. El camino que se retorna no lleva la barra inclinada al nal, pero, como hemos visto, a la funcin os.path.join() no le afecta. o 4. Lnea 10: Si combinamos estas tcnicas podemos construir fcilmente caminos e a completos desde el directorio ra del usuario. La funcin os.path.join() puede z o recibir cualquier nmero de parmetros. Yo me alegr mucho al descubrir esto u a e puesto que la funcin anyadirBarra() es una de las t o picas que siempre tengo que escribir cuando aprendo un lenguaje de programacin nuevo. No escribas o esta estpida funcin en Python, personas inteligentes se ha ocupado de ello u o por ti.

El mdulo os.path tambin contiene funciones para trocear caminos completos, o e nombres de directorios y nombres de chero en sus partes constituyentes.
1 2 3 4 5 6 7 8 9 10 11 12 13 >>> nombrepath = /home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / parahumanos . py >>> o s . path . s p l i t ( nombrepath ) ( /home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / e j e m p l o s , parahumanos . py ) >>> ( nombredir , n o m b r e f i c h ) = o s . path . s p l i t ( nombrepath ) >>> nombredir /home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / e j e m p l o s >>> n o m b r e f i c h parahumanos . py >>> ( nombrecorto , e x t e n s i o n ) = o s . path . s p l i t e x t ( n o m b r e f i c h ) >>> nombrecorto parahumanos >>> e x t e n s i o n . py

1. Lnea 2: La funcin split() divide un camino completo en dos partes que con o tienen el camino y el nombre de chero. Los retorna en una tupla. 2. Lnea 4: Recuerdas cuando dije que pod utilizar la asignacin mltiple para as o u devolver varios valores desde una funcin? os.path.split() hace exactamente eso. o Puedes asignar los valores de la tupla que retorna a dos variables. Cada variable recibe el valor que le corresponde al elemento de la tupla. 3. Lnea 5: La primera variable, nombredir, recibe el valor del primer elemento de la tupla que retorna os.path.split(), el camino al chero. 4. Lnea 7: La segunda variable, nombrech, recibe el valor del segundo elemento de la tupla que retorna os.path.split(), el nombre del chero.

3.2. TRABAJAR CON FICHEROS Y DIRECTORIOS

87

5. Lnea 9: os.path tambin posee la funcin os.path.splitext() que divide el nom e o bre de un chero en una tupla que contiene el nombre y la extensin separados o en dos elementos. Puedes utilizar la misma tcnica que antes para asignarlos e a dos variables separadas.

3.2.3.

Listar directorios

El mdulo glob es otra herramienta incluida en la librer estndar de Python. o a a Proporciona una forma sencilla de acceder al contenido de un directorio desde un programa. Utiliza los caracteres comodn que suelen usarse en una consola de l nea de comandos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 >>> o s . c h d i r ( /home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / ) >>> import g l o b >>> g l o b . g l o b ( e j e m p l o s / . xml ) [ e j e m p l o s \\ f e e d broken . xml , e j e m p l o s \\ f e e d ns0 . xml , e j e m p l o s \\ f e e d . xml ] >>> o s . c h d i r ( e j e m p l o s / ) >>> g l o b . g l o b ( t e s t . py ) [ a l p h a m e t i c s t e s t . py , p l u r a l t e s t 1 . py , p l u r a l t e s t 2 . py , p l u r a l t e s t 3 . py , p l u r a l t e s t 4 . py , p l u r a l t e s t 5 . py , p l u r a l t e s t 6 . py , r o m a n t e s t 1 . py , r o m a n t e s t 1 0 . py , r o m a n t e s t 2 . py , r o m a n t e s t 3 . py , r o m a n t e s t 4 . py , r o m a n t e s t 5 . py , r o m a n t e s t 6 . py , r o m a n t e s t 7 . py , r o m a n t e s t 8 . py , r o m a n t e s t 9 . py ]

1. Lnea 3: El mdulo glob utiliza comodines y devuelve el camino de todos los o cheros y directorios que coindicen con la bsqueda. En este ejemplo se busca un u directorio que contenga cheros terminados en *.xml, lo que encontrar toa dos los cheros xml que se encuentren en el directorio de ejemplos. 2. Lnea 7: Ahora cambio el directorio de trabajo al subdirectorio ejemplos. La

88

CAP ITULO 3. COMPRENSIONES funcin os.chdir() puede recibir como parmetro un camino relativo a la posio a cin actual. o 3. Lnea 8: Puedes incluir varios comodines de bsqueda. El ejemplo encuentra u todos los cheros del directorio actual de trabajo que incluyan la palabra test en alguna parte del nombre y que, adems, terminen con la cadena .py. a

3.2.4.

Obtener metadatos de cheros

Todo sistema de cheros moderno almacena metadatos sobre cada chero: fecha de creacin, fecha de la ultima modicacin, tamao, etc. Python proporciona o o n una API unicada para acceder a estos metadatos. No necesitas abrir el chero, unicamente necesitas su nombre.
1 2 3 4 5 6 7 8 9 10 >>> import o s >>> print ( o s . getcwd ( ) ) /home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / e j e m p l o s >>> metadata = o s . s t a t ( f e e d . xml ) >>> metadata . st mtime 1247520344.9537716 >>> import time >>> time . l o c a l t i m e ( metadata . st mtime ) time . s t r u c t t i m e ( tm year =2009 , tm mon=7, tm mday=13 , tm hour =17 , tm min =25 , t m s e c =44 , tm wday=0, tm yday =194 , t m i s d s t =1)

1. Lnea 2: El directorio de trabajo actual es ejemplos. 2. Lnea 4: feed.xml es un chero que se encuentra en el directorio ejemplos. La funcin os.stat() devuelve un objeto que contiene diversos metadatos sobre el o chero. 3. Lnea 5: st mtime contiene la fecha y hora de modicacin, pero en un formato o que no es muy util (Tcnicamente es el nmero de segundos desde el inicio de e u la Epoca, que est denida como el primer segundo del 1 de enero de 1970 En a serio!). 4. L nea 7: El mdulo time forma parte de la librer estndar de Python. Cono a a tiene funciones para convertir entre diferentes representaciones del tiempo, formatear valores de tiempo en cadenas y manipular las referencias a los husos horarios. 5. Lnea 8: La funcin time.localtime() convierte un valor de segundos desde el o inicio de la poca (que procede la propiedad anterior) en una estructura ms e a

3.3. LISTAS POR COMPRENSION

89

util que contiene ao, mes, d hora, minuto, segundo, etc. Este chero se n a, modic por ultima vez el 13 de julio de 2009 a las 5:25 de la tarde. o
1 2 3 4 5 6 # c o n t i n u a c i n d e l e j e m p l o a n t e r i o r o >>> metadata . s t s i z e 3070 >>> import parahumanos >>> parahumanos . tamnyo aproximado ( metadata . s t s i z e ) 3 . 0 KiB

1. Lnea 2: La funcin os.stat() tambin devuelve el tamao de un chero en la o e n propiedad st size. El chero feed.xml ocupa 3070 bytes. 2. Lnea 5: Aprovecho la funcin tamanyo aproximado() para verlo de forma ms o a clara.

3.2.5.

Construccin de caminos absolutos o

En el apartado anterior, se observ cmo la funcin glob.glob() devolv una o o o a lista de nombres relativa. El primer ejemplo mostraba caminos como ejemplos/feed.xml, y el segundo ejemplo incluso ten nombres ms cortos como romana a test1.py. Mientras permanezcas en el mismo directorio de trabajo los path relativos funcionarn sin problemas para recuperar informacin de los cheros. No obstante, si a o quieres construir un camino absoluto Uno que contenga todos los directorios hasta el ra del sistema de archivos lo que necesitas es la funcin os.path.realpath(). z o
1 >>> import o s 2 >>> print ( o s . getcwd ( ) ) 3 /home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / e j e m p l o s 4 >>> print ( o s . path . r e a l p a t h ( f e e d . xml ) ) 5 /home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / e j e m p l o s / f e e d . xml

3.3.

Listas por comprensin o

La creacion de listas por comprensin proporciona una forma compacta de o crear una lista a partir de otra mediante la realizacin de una operacin a cada uno o o de los elementos de la lista original.

90
1 2 3 4 5 6 7 8 >>> >>> [2 , >>> [1 , >>> >>> [2 ,

CAP ITULO 3. COMPRENSIONES


una lista = [1 , 9 , 8 , 4] [ elem 2 f o r elem in u n a l i s t a ] 18 , 16 , 8 ] una lista 9 , 8 , 4] u n a l i s t a = [ elem 2 f o r elem in u n a l i s t a ] una lista 18 , 16 , 8 ]

1. Lnea 2: Para explicar esto es mejor leerlo de derecha a izquierda. una lista es la lista origen que se va a recorrer para generar la nueva lista. El intrprete e de Python recorre cada uno de los elementos de una lista, asignando temporalmente el valor de cada elemento a la variable elem. Despus Python aplica la e operacin que se haya indicado, en este caso elem * 2, y el resultado lo aade o n a la nueva lista. 2. Lnea 4: Como se observa, la lista original no cambia. 3. Lnea 6: No pasa nada por asignar el resultado a la variable que ten la lista a original. Python primero construye la nueva lista en memoria y luego asigna el resultado a la variable. Para crear una lista de esta forma, puedes utilizar cualquier expresin vlida o a de Python, como por ejemplo las funciones del mdulo os para manipular cheros o y directorios.
1 >>> import os , g l o b 2 >>> g l o b . g l o b ( . xml ) 3 [ f e e d broken . xml , f e e d ns0 . xml , f e e d . xml ] 4 >>> [ o s . path . r e a l p a t h ( f ) f o r f in g l o b . g l o b ( . xml ) ] 5 [ /home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / e j e m p l o s / f e e d broken . xml , 6 /home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / e j e m p l o s / f e e d ns0 . xml , 7 /home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / e j e m p l o s / f e e d . xml ]

1. Lnea 2: Esta llamada retorna una lista con todos los cheros terminados en .xml del directorio de trabajo. 2. Lnea 4: Esta lista generada por comprensin toma la lista original y la trans o forma en una nueva lista con los nombres completos de ruta. Las listas por comprensin tambin permiten ltrar elementos, generando una o e lista cuyo tamao sea menor que el original. n

3.3. LISTAS POR COMPRENSION


1 >>> import os , g l o b 2 >>> [ f f o r f in g l o b . g l o b ( . py ) i f o s . s t a t ( f ) . s t s i z e > 6 0 0 0 ] 3 [ p l u r a l t e s t 6 . py , 4 r o m a n t e s t 1 0 . py , 5 r o m a n t e s t 6 . py , 6 r o m a n t e s t 7 . py , 7 r o m a n t e s t 8 . py , 8 r o m a n t e s t 9 . py ]

91

1. Lnea 2: Para ltrar una lista puedes incluir la clasula if al nal de la com u prensin. Esta expresin se evala para cada elemento de la lista original. Si o o u el resultado es verdadero, el elemento ser calculado e incluido en el resultado. a En este caso se seleccionan todos los cheros que terminan en .py que se encuentren en el directorio de trabajo, se comprueba si son de tamao mayor a n 6000 bytes. Seis de ellos cumplen este requisito, por lo que son los que aparecen en el resultado nal. Hasta el momento, todos los ejemplos de generacin de listas por comprensin o o han utilizado expresiones muy sencillas multiplicar un nmero por una constante, u llamada a una funcin o simplemente devolver el elemento original de la lista pero o no existe l mite en cuanto a la complejidad de la expresin. o
1 2 3 4 5 6 7 8 9 10 11 >>> import os , g l o b >>> [ ( o s . s t a t ( f ) . s t s i z e , o s . path . r e a l p a t h ( f ) ) f o r f in g l o b . g l o b ( . xml ) ] [ ( 3 0 7 4 , c : / home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / e j e m p l o s / f e e d broken . xml ) , ( 3 3 8 6 , c : / home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / e j e m p l o s / f e e d ns0 . xml ) , ( 3 0 7 0 , c : / home/ j m g a g u i l e r a / i n m e r s i o n e n p y t h o n 3 / e j e m p l o s / f e e d . xml ) ] >>> import parahumanos >>> [ ( parahumanos . tamanyo aproximado ( o s . s t a t ( f ) . s t s i z e ) , f ) f o r f in g l o b . g l o b ( . xml ) ] [ ( 3 . 0 KiB , f e e d broken . xml ) , ( 3 . 3 KiB , f e e d ns0 . xml ) , ( 3 . 0 KiB , f e e d . xml ) ]

1. Lnea 2: En este caso se buscan los cheros que nalizan en .xml en el directorio de trabajo actual, se recupera su tamao (mediante una llamada a la funcin n o os.stat()) y se construye una tupla con el tamao del chero y su ruta completa n (mediante una llamada a os.path.realpath(). 2. Lnea 7: En este caso se aprovecha la lista anterior para generar una nueva con el tamao aproximado de cada chero. n

92

CAP ITULO 3. COMPRENSIONES

3.4.
lista.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

Diccionarios por comprensin o

Es similar al apartado anterior pero genera un diccionario en lugar de una


>>> import os , g l o b >>> metadata = [ ( f , o s . s t a t ( f ) ) f o r f in g l o b . g l o b ( t e s t . py ) ] >>> metadata [ 0 ] ( a l p h a m e t i c s t e s t . py , nt . s t a t r e s u l t ( st mode =33206 , s t i n o =0, s t d e v =0, s t n l i n k =0, s t u i d =0, s t g i d =0, s t s i z e =2509 , s t a t i m e =1247520344 , st mtime =1247520344 , s t c t i m e =1247520344)) >>> m e t a d a t a d i c t = { f : o s . s t a t ( f ) f o r f in g l o b . g l o b ( t e s t . py ) } >>> type ( m e t a d a t a d i c t ) <c l a s s d i c t > >>> l i s t ( m e t a d a t a d i c t . k e y s ( ) ) [ r o m a n t e s t 8 . py , p l u r a l t e s t 1 . py , p l u r a l t e s t 2 . py , p l u r a l t e s t 5 . py , p l u r a l t e s t 6 . py , r o m a n t e s t 7 . py , r o m a n t e s t 1 0 . py , r o m a n t e s t 4 . py , r o m a n t e s t 9 . py , p l u r a l t e s t 3 . py , r o m a n t e s t 1 . py , r o m a n t e s t 2 . py , r o m a n t e s t 3 . py , r o m a n t e s t 5 . py , r o m a n t e s t 6 . py , a l p h a m e t i c s t e s t . py , p l u r a l t e s t 4 . py ] >>> m e t a d a t a d i c t [ a l p h a m e t i c s t e s t . py ] . s t s i z e 2509

1. Lnea 2: Esto no genera un diccionario por comprensin, genera una lista por o comprensin. Encuentra todos los cheros terminados en .py con el texto test o en el nombre y luego construye una tupla con el nombre y los metadatos del chero (llamando a la funcin os.stat()). o 2. Lnea 3: Cada elemento de la lista resultante es una tupla. 3. Lnea 7: Esto s es una generacin de un diccionario por comprensin. La sin o o taxis es similar a la de la generacin de listas, con dos diferencias: primero, o se encierra entre llaves en lugar de corchetes; segundo, en lugar de una unica expresin para cada elemento, contiene dos expresiones separadas por dos puno tos. La expresin que va delante de los dos puntos es la clave del diccionario o y la expresin que va detrs es el valor (os.stat(f) en este ejemplo). o a 4. Lnea 8: El resultado es un diccionario. 5. Lnea 10: La claves de este caso particular son los nombres de los cheros. 6. Lnea 16: El valor asociado a cada clave es el valor que retorn la funcin o o os.stat(). Esto signica que podemos utilizar este diccionario para buscar los metadatos de un chero a partir de su nombre. Uno de los elementos de estos n metadatos es st size, el tamao de chero. Para el chero alphameticstest.py el valor es 2509 bytes.

3.5. CONJUNTOS POR COMPRENSION

93

Como con las listas, puedes incluir la clasula if para ltrar los elementos de u entrada mediante una expresin que se evala para cada uno de los elementos. o u
1 2 3 4 5 6 7 8 9 >>> import os , glob , parahumanos >>> d i c t = { o s . path . s p l i t e x t ( f ) [ 0 ] : parahumanos . tamanyo aproximado ( os . s t a t ( f ) . s t s i z e ) \ ... f o r f in g l o b . g l o b ( ) i f o s . s t a t ( f ) . s t s i z e > 6000} >>> l i s t ( d i c t . k e y s ( ) ) [ romantest9 , romantest8 , romantest7 , romantest6 , romantest10 , p l u r a l t e s t 6 ] >>> d i c t [ r o m a n t e s t 9 ] 6 . 5 KiB

1. Lnea 4: Este ejemplo construye una lista con todos los cheros del directorio de trabajo actual (glob.glob(*)), ltra la lista para incluir unicamente aquellos cheros mayores de 6000 bytes (if os.stat(f).s size 6000) y utiliza la lista ltrada para construir un diccionario cuyas claves son los nombres de chero menos la extensin (os.path.splitext(f)[0]) y los valores el tamao de cada uno o n de ellos (parahumanos.tamanyo aproximado(os.stat(f).st size)). 2. Lnea 5: Como viste en el ejemplo anterior son seis cheros, por lo que hay seis elementos en el diccionario. 3. Lnea 7: El valor de cada elemento es la cadena que retorna la funcin taman o yo aproximado().

3.4.1.

Trucos que se pueden hacer

Te presento un truco que puede serte de utilidad: intercambiar las claves y valores de un diccionario.
1 >>> d i c t = { a : 1 , b : 2 , c : 3} 2 >>> { v a l u e : key f o r key , v a l u e in a d i c t . i t e m s ( ) } 3 {1: a , 2: b , 3: c }

3.5.

Conjuntos por comprensin o

Por ultimo mostrar la sintaxis para generar conjuntos por comprensin. Es e o muy similiar a la de los diccionarios, con la unica diferencia de que unicamente se incluyen valores en lugar de parejas clave-valor.

94
1 2 3 4 5 6 7 8 9 >>> c o n j u n t o = s e t ( r a n g e ( 1 0 ) ) >>> c o n j u n t o { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9} >>> {x 2 f o r x in c o n j u n t o } { 0 , 1 , 4 , 8 1 , 6 4 , 9 , 1 6 , 4 9 , 2 5 , 36} >>> {x f o r x in c o n j u n t o i f x % 2 == 0} { 0 , 8 , 2 , 4 , 6} >>> {2 x f o r x in r a n g e ( 1 0 ) } { 3 2 , 1 , 2 , 4 , 8 , 6 4 , 1 2 8 , 2 5 6 , 1 6 , 512}

CAP ITULO 3. COMPRENSIONES

1. Lnea 4: Los conjuntos generados por comprensin pueden partir de otro con o junto en lugar de una lista. En este ejemlo se calcula el cuadrado de cada uno de los elementos (los nmeros del 0 al 9). u 2. Lnea 6: Como en el caso de las listas y diccionarios, puedes incluir una clasula u if para ltrar elementos antes de calcularlos e incluirlos en el resultado. 3. L nea 8: Los conjuntos por comprensin no necesitan tomar un conjunto como o entrada, pueden partir de cualquier tipo de secuencia.

3.6.

Lecturas complementarias

mdulo os o os Portabilidad en el acceso a caracter sticas espec cas del sistema operativo mdulo os.path o Manipulacin de los nombres de chero independiente de la plataforma o os.path mdulo glob o Patrones de bsqueda de cheros glob u mdulo time o Funciones para manipulacin de hora time o Listas por comprensin o Comprensiones anidadas Tcnicas para hacer bucles e

Cap tulo 4 Cadenas de texto


Nivel de dicultad:   Te digo esto porque eres uno de mis amigos, Mi vocabulario comienza donde el tuyo termina! Dr. Seuss, On beyond Zebra!

4.1.

Temas aburridos que debes conocer antes de la inmersin o

Sab que la gente de Bougainville tiene el alfabeto ms pequeo del mundo? as a n El alfabeto Rotokas est compuesto unicamente por 12 letras: A, E, G, I, K, O, P, a R, S, T, U y V. En el otro lado del espectro los lenguajes como el Chino, Japons e y Koreano tienen miles de caracteres. El ingls, desde luego, tiene 26 letras 52 e si cuentas las maysculas y minsculas de forma separada ms un puado de u u a n s mbolos de puntuacin !@#$ %&?. o Cuando las personas hablan sobre texto piensan en caracteres y s mbolos en la pantalla del ordenador. Pero los ordenadores no conocen ni s mbolos ni caracteres, conocen bits y bytes. Cada elemento textual que ves en la pantalla est almacenado con una codicacin de caracteres particular. Explicndolo de a o a manera informal, la codicacin de caracteres proporciona una conversin entre lo o o que ves en la pantalla y lo que el ordenador realmente almacena en memoria o en disco. Existen muchas codicaciones de caracteres diferentes, algunas optimizadas para determinados lenguajes como el ruso, el chino o el ingls, y otras que se pueden e utilizar para diferentes lenguajes.

95

96

CAP ITULO 4. CADENAS DE TEXTO

En realidad es ms complicado. Muchos caracteres son comunes a diferentes a codicaciones, pero cada codicacin puede utilizar una secuencia de bytes diferente o para almacenar esos caracteres en memoria o disco. Puedes imaginarte que una codicacin de caracteres es como una especia de clave de desencriptado. Cuando tengas o una secuencia de bytes un chero, una pgina web o cualquier otra cosa y se a considere que esos bytes representan texto, necesitas conocer en qu codicacin e o de caracteres se encuentra para poder decodicar los bytes y conocer a qu carace teres representan. Si tienes una clave de decodicacin equivocada o no dispones de o ninguna, la decodicacin no ser posible o ser errnea (si se usa una decodicacin o a a o o equivocada), y el resultado ser un texto sin sentido. a Seguramente habrs visto a veces paginas a web con extraos caracteres de interrogacin o n o Todo lo que que pensabas que similar, en donde esperabas algn caracter cou sab sobre las cadenas de as mo el apstrofo o vocales acentuadas. Esto suele o texto es errneo. o indicar que el autor de la pgina no declar coa o rrectamente la codicacin de caracteres que utio liz por lo que tu navegador la tiene que adivinar y el resultado es una mezcla de o caracteres esperados e inesperados. En ingls esto es simplemente desconcertante, e pero en otros lenguajes el resultado puede ser completamente ilegible. Existen tablas de codicacin de caracteres para cada uno de los lenguajes o importantes del mundo. Puesto que cada lenguaje es diferente, y la memoria y el espacio en disco ha sido caro histricamente, cada tabla de codicacin de caracteres o o est optimizada para un lenguaje en particular. Lo que quiero decir con esto es que a cada una de las codicaciones usa los mismos nmeros (0 - 255) para representar los u caracteres de un lenguaje determinado. Por ejemplo, posiblemente ests familiarizae do con la codicacin ASCII, que almacena los caracteres del ingls como nmeros o e u que van del 0 al 127 (65 es la A, 97 es la a, etc). El ingls es un alfabeto muy e simple, por lo que puede expresarse con menos de 128 nmeros. Para aquellos que u sepan contar en base 2, eso signica 7 bits de los 8 de un byte. Algunos lenguajes de Europa como el francs, espaol y alemn necesitan ms e n a a letras que el ingls. O, para ser ms precisos, tienen letras que se combinan con e a diversas marcas diacr ticas, como el carcter n del espaol. La tabla de codicacin a n o de caracteres ms comn para estos lenguajes es CP-1252, que tambin es conocida a u e como windows-1252 porque se utiliza ampliamente en el sistema operativo Microsoft Windows. La codicacin CP-1252 comparte con ASCII los primeros 128 caracteres o (0-127), pero luego se extiende en el rango de 128 a 255 para los caracteres restantes (241 es la , 252 es la , etc). Contina siendo una tabla de codicacin de un n u u o unico byte. El valor mayor, 255, an cabe en un byte. u Adems existen lenguajes como el chino, japons y coreano, que tienen tantos a e

4.1. TEMAS ABURRIDOS QUE DEBES CONOCER ANTES DE LA INMERSION97 caracteres que requieren tablas de codicacin de caracteres multibyte. Esto signica o que cada carcter se representa como un nmero de dos bytes lo que abarca del 0 a u al 65535. Pero las codicaciones multibyte tambin tienen el mismo problema que e las diferentes codicaciones de un unico byte: cada una de ellas puede utilizar el mismo nmero para expresar un carcter diferente. La unica diferencia entre ellas u a es que el rango de caracteres disponible es mayor en las codicaciones multibyte. Esto no supon demasiado problema en un mundo desconectado, en donde a texto era algo que tecleabas para t y ocasionalmente imprim No exist mucho as. a texto plano. El cdigo fuente era ASCII y todo el mundo usaba procesadores o de textos que den su propo formato que ten en cuenta la informacin de an an o codicacin de caracteres junto con la informacin de estilo, etc. La gente le estos o o a documentos con el mismo programa procesador de texto que el autor original, por lo que todo funcionaba, ms o menos. a Ahora piensa en la aparicin de las redes globales y en el correo y la web. Mucho o texto plano anda suelto por el mundo, se crea en un ordenador, se transmite a un segundo y se muestra en un tercero. Los ordenadores unicamente distinguen nme u ros, pero los nmeros pueden signicar cosas diferentes. Oh no! Qu hacer? Bien, u e los sistemas tuvieron que disearse para transportar la informacin de codicacin n o o junto con el texto plano. Recuerda, se trata de las claves de descodicacin que o mapean los nmeros entendidos por el ordenador a caracteres legibles por personas. u Una clave de descodicacin perdida da lugar a texto ilegible. o Ahora piensa en intentar almacenar diversos documentos de texto en el mismo lugar, como en una tabla de una misma base de datos que almacena todo el correo electrnico que hayas recibido. An necesitas almacenar la codicacin de caracteres o u o junto con cada correo electrnico para que se pueda leer apropiadamente. Parece o dif cil? Prueba a buscar en tu base de datos de correos, eso signica convertir entre mltiples tablas de codicacin de caracteres sobre la marcha. No suena divertido?. u o Piensa ahora en la posibilidad de documentos multil ngues, en donde aparecen 1 caracteres en diferentes lenguajes . Y, por supuesto, querrs buscar el contenido de a esos documentos. Ahora llora un rato, porque todo lo que cre conocer sobre las cadenas de as texto es errneo, y no existe algo as como el texto plano. o
Pista: los programas que han intentando hacer esto utilizan habitualmente cdigos de escape o para conmutar entre modos. Si ests en modo ruso koi8-r el cdigo 241 signica R. Si cambias a o
1

a modo Griego el cdigo 241 signica . o

98

CAP ITULO 4. CADENAS DE TEXTO

4.2.

Unicode

Entra en Unicode. Unicode es un sistema diseado para representar todos los caracteres de todos n los lenguajes. Representa cada letra, carcter o ideograma como un nmero de cuatro a u bytes. Cada nmero representa a un unico carcter que se use en al menos uno de u a los lenguajes del mundo (No se usan todos los nmeros, pero se usan ms de 65535 u a por lo que no es suciente utilizar dos bytes). Los caracteres que se utilizan en diferentes lenguajes tienen el mismo nmero generalmente, a menos que exista una u buena razn etimolgica para que no sea as De todos modos hay exactamente un o o . nmero por cada carcter y un carcter por nmero. De esta forma, cada nmero u a a u u siempre signica una unica cosa. No existen modos que rastrear, U+0041 siempre corresponde a A, incluso si tu lenguaje no usa tal s mbolo. A primera vista parece una gran idea, una tabla de codicacin de caracteres o para gobernarlos a todos. Mltiples lenguajes por documento, no ms cambios de u a modo para conmutar entre tablas de codicacin en medio de un documento. Pero o existe una pregunta obvia. Cuatro bytes? Para cada carcter? Parece un gasto a intil la mayor parte de las ocasiones, especialmente para idiomas como el ingls o u e el espaol, que necesitan menos de un byte (256 nmeros) para expresar cada uno n u de los caracteres posibles. De hecho, es un desperdicio de espacio incluso para los lenguajes basados en ideogramas como el chino, que nunca necesitan ms de dos a caracteres para cada carcter. a Existe una tabla de codicacin Unicode que utiliza cuatro bytes por cada o carcter. Se denomina UTF-32 porque 32 bits es igual a 4 bytes. UTF-32 es una a codicacin directa; toma cada carcter Unicode (un nmero de 4 bytes) y repreo a u senta al carcter con ese mismo nmero. Esto tiene varias ventajas siendo la ms a u a importante que puedes encontrar el ensimo carcter de una cadena en un tieme a po constante ya que se encuentra en a partir del byte 4 n. Tambin tiene varios e inconvenientes, siendo el ms obvio que necesita cuatro bytes para almacenar cada a carcter. a Incluso aunque existen muchos caracteres Unicode, resulta que la mayor de la a gente nunca usar nada ms all de los primeros 65535. Por eso existe otra codicaa a a cin Unicode denominada UTF-16 (16 bits son 2 bytes) que codica cada uno de los o caracteres de 0 a 65535 como dos bytes. Adems utiliza algunos trucos sucios por a si necesitas representar aquellos caracteres que se usan raramente y que estn ms a a all del 65535. La ventaja ms obvia es que esta tabla de codicacin de caracteres a a o es el doble de eciente que la de UTF-32 puesto que cada carcter requiere unicaa mente dos bytes para almacenarse, en lugar de cuatro bytes. An se puede encontrar u fcilmente el ensimo carcter de una cadena en un tiempo constante, siempre que a e a

4.2. UNICODE

99

se asuma que no existen caracteres especiales de los que estn por encima de 65535. a Lo que suele ser una buena asuncin... hasta el momento en que no lo es! o Tambin existen algunos inconvenientes no tan obvios tanto en UFT-32 y UTFe 8. Los ordenadores de sistemas diferentes suelen almacenar los bytes de diferentes formas. Esto signica que el carcter U+4E2D podr almacenarse en UTF-16 bien a a como 4E 2D o como 2D 4E, dependiendo de que el sistema sea big-endian o littleendian2 (para UTF-32 existen ms posibilidades de ordenacin de los bytes). Miena o tras tus documentos no dejen tu ordenador ests seguro las diferentes aplicaciones a del mismo ordenador utilizarn la misma ordenacin de bytes. Pero en el momento a o que transeras los documentos entre sistemas, tal vez a travs de Internet, vas a e necesitar una forma de indicar el orden en el que se han almacenado los bytes. De otra forma el sistema que recibe los datos no tiene forma de saber si la secuencia de dos bytes 4E 2D signica U+4E2D o U+2D4E. Para resolver este problema, las codicaciones multibyte de Unicode denen el Byte Orden Mark (BOM)3 , que es un carcter especial no imprimible que se a puede incluir al comienzo de tu documento para indica qu ordenacin de bytes e o tiene el documento. Para UTF-16 la marca de ordenacin de bytes es U+FEFF, por o lo que si recibes un documento UTF-16 que comienza con los bytes FF FE, ya sabes en qu forma vienen ordenados los bytes; si comienza con FE FF sabes que el orden e es el contrario. An as UTF-16 no es exactamente el ideal, especialmente si la mayor parte u , de los caracteres que utilizas son ASCII. Si lo piensas, incluso una pgina web china a contendr muchos caracteres ASCII todos los elementos y atributos que rodean a a los caracteres imprimibles chinos. Poder encontrar el ensimo carcter est bien, e a a pero existe el problema de los caracteres que requieren ms de dos bytes, lo que a signica que no puedes garantizar que todos los caracteres ocupan exactamente dos bytes, por lo que en la prctica no puedes encontrar el carcter de la posicin a a o ensima en un tiempo constante a no ser que mantengas un e ndice separado. Y muchacho, te aseguro que hay mucho texto ASCII por el mundo... Otras personas valoraron estas preguntas y plantearon una solucin: o

UTF-8
UTF-8 es un sistema de codicacin de longitud variable para Unicode. Eso to signica que los caracteres pueden utilizar diferente nmero de bytes. Para los u caracteres ASCII utiliza un unico byte por carcter. De hecho, utiliza exactamente a
2 3

Almacene los bytes en orden o invirtiendo el mismo. Nota del traductor: Marca de ordenacin de bytes. o

100

CAP ITULO 4. CADENAS DE TEXTO

los mismos bytes que ASCII por lo que los 128 primeros caracteres son indistinguibles. Los caracteres latinos extendidos como la n o la o utilizan dos bytes4 . Los caracteres chinos utilizan tres bytes, y los caracteres ms raros utilizan cuatro. a Desventajas: debido a que los caracteres pueden ocupar un nmero diferente u de bytes, encontrar el carcter de la posicin ensima es una operacin de orden a o e o de complejidad O(n) lo que signica que cuanto ms larga sea la cadena, ms a a tiempo lleva encontrar un carcter espec a co. Asimismo, hay que andar codicando y decodicando entre bytes y caracteres. Ventajas: se trata de una codicacin supereciente de los caracteres ASCII. o No es peor que UTF-16 para los caracteres latinos extendidos, y es mejor que UTF32 para los caracteres chinos. Tambin (aqu tendrs que conar en mi, porque no e a te voy a mostrar la matemtica involucrada), debido a la naturaleza exacta de la a manipulacin de los bits, no existen problemas de ordenacin de bits. Un documento o o codicado en UTF-8 siempre contiene la misma cadena de bytes sea cual sea el ordenador y sistema operativo.

4.3.

Inmersin o

En Python 3 todas las cadenas de texto son secuencias de caracteres Unicode. En Python 3 no existen cadenas codicadas en UTF-8 o en CP-1252. No es correcto decir Es esta cadena una cadena codicada en UTF-8?. UTF-8 es una forma de codicar caracteres en una secuencia de bytes. Si quieres convertir una cadena de caracteres en una secuencia de bytes en una codicacin de caracteres particular, o Python 3 puede ayudarte con ello. Si quieres tomar una secuencia de bytes y convertirla en una cadena de texto, tambin te puede ayudar Python 3. Los bytes no e son caracteres, los bytes son bytes. Los caracteres son una abstraccin. Una cadena o es una secuencia de esas abstracciones.
1 2 3 4 5 6 7 >>> s = Python >>> l e n ( s ) 9 >>> s [ 0 ] >>> s + 3 Python 3

1. Lnea 1: Para crear una cadena de texto debes utilizar las comillas para deli mitarla. Se pueden utilizar comillas simples () o dobles ().
Los bytes no son unicamente la codicacin de Unicode como sucede en UTF-16, se efectan o u diversos cambios para obtener la codicacin en UTF-8. o
4

4.4. FORMATEAR CADENAS

101

2. Lnea 2: La funcin interna len() devuelve la longitud de la cadena, el nmero o u de caracteres. Esta funcin es la misma que utilizas para conocer la longitud o de una lista, tupla, conjunto o diccionario. Una cadena es como una tupla de caracteres. 3. Lnea 4: De la misma forma que puedes obtener elementos individuales de una lista, puedes obtener caracteres individuales de una cadena mediante la notacin de o ndices. 4. Lnea 6: Como con las listas y tuplas, puedes contatenar las cadenas utilizando el operador +.

4.4.

Formatear cadenas
Las cadenas de texto se pueden denir utilizando comillas simples o dobles.

Vamos a echarle otro vistazo a parahumanos.py:

102

CAP ITULO 4. CADENAS DE TEXTO

1 # parahumanos . py 2 3 SUFIJOS = { 1 0 0 0 : [ KB , MB , GB , TB , PB , EB , ZB , YB ] , 4 1 0 2 4 : [ KiB , MiB , GiB , TiB , PiB , EiB , ZiB , 5 YiB ] } 6 7 def tamanyo aproximado ( tamanyo , u n k i l o b y t e e s 1 0 2 4 b y t e s=True ) : 8 C o n v i e r t e un tama o de f i c h e r o en formato l e g i b l e por p e r s o n a s n 9 10 Argumentos / par metros : a 11 tamanyo tama o de f i c h e r o en b y t e s n 12 u n k i l o b y t e e s 1 0 2 4 b y t e s s i True ( por d e f e c t o ) , 13 usa m l t i p l o s de 1024 u 14 s i F a l s e , usa m l t i p l o s de 1000 u 15 16 retorna : string 17 18 19 i f tamanyo < 0 : 20 r a i s e V a l u e E r r o r ( e l n mero debe s e r no n e g a t i v o ) u 21 22 m u l t i p l o = 1024 i f u n k i l o b y t e e s 1 0 2 4 b y t e s e l s e 1000 23 f o r s u f i j o in SUFIJOS [ m u l t i p l o ] : 24 tamanyo /= m u l t i p l o 25 i f tamanyo < m u l t i p l o : 26 return { 0 : . 1 f } {1} . format ( tamanyo , s u f i j o ) 27 28 r a i s e V a l u e E r r o r ( n mero demasiado grande ) u 29 30 i f name == m a i n : 31 print ( tamanyo aproximado ( 1 0 0 0 0 0 0 0 0 0 0 0 0 , F a l s e ) ) 32 print ( tamanyo aproximado ( 1 0 0 0 0 0 0 0 0 0 0 0 0 ) )

1. Lnea 3: KB, MB, GB, ... son cadenas. 2. Lnea 8: Las cadenas de documentacin (docstrings) son cadenas de texto. o Como se expanden ms all de una l a a nea se utilizan tres comillas al comienzo y al nal para delimitarlas. 3. Lnea 18: Estas comillas triples nalizan la cadena de documentacin de esta o funcin. o 4. Lnea 20: Otra cadena que se pasa como parmetro a la excepcin con el n a o de que sea legible por una persona. 5. Lnea 26: Esto es... U! Qu car.. es esto? e

4.4. FORMATEAR CADENAS

103

Python 3 permite formatear valores en cadenas de texto. Aunque este sistema permite expresiones muy complejas, su uso ms bsico consiste en insertar un valor a a en una cadena de texto en el lugar denido por un marcador.
1 >>> u s u a r i o = mark 2 >>> c l a v e = PapayaWhip 3 >>> La c l a v e de {0} e s {1} . format ( u s u a r i o , c l a v e ) 4 La c l a v e de mark e s PapayaWhip

1. Lnea 2: Realmente mi clave no es PapayaWhip. 2. Lnea 3: Aqu hay mucho que contar. Primero, se observa una llamada a un mtodo sobre una cadena de texto. Las cadenas de texto son objetos, y los e objetos tienen mtodos, como ya sabes. Segundo, la expresin completa se e o evala a una cadena. Tercero, {0} y {1} son campos de reemplazo, que se u sustituyen con los parmetros que se pasen al mtodo format(). a e

4.4.1.

Nombres de campos compuestos

En el ejemplo anterior se muestra el ejemplo ms simple, aqul en el que los a e campos de reemplazo son nmeros enteros. Los campos de reemplazo enteros se u tratan como ndices posicionales en la lista de parmetros del mtodo format(). Eso a e signica que el {0} se reemplaza por el primer parmetro (usuario en este caso), {1} a se reemplaza por el segundo (clave en este caso), etc. Puedes utilizar tantos campos de reemplazo posicional como parmetros se pasen en el mtodo format(). Pero los a e campos de reemplazo permiten mucha ms funcionalidad. a
1 2 3 4 5 6 >>> import parahumanos >>> m i s s u f i j o s = parahumanos . SUFIJOS [ 1 0 0 0 ] >>> m i s s u f i j o s [ KB , MB , GB , TB , PB , EB , ZB , YB ] >>> 1 0 0 0 { 0 [ 0 ] } = 1 { 0 [ 1 ] } . format ( m i s s u f i j o s ) 1000KB = 1MB

1. Lnea 2: En lugar de ejecutar una funcin del mdulo parahumanos unicamente o o ests capturando una de las estructuras de datos que dene; la lista de sujos a que representan las potencias de 1000. 2. Lnea 5: Esta l nea parece complicada, pero no lo es. {0} representa el primer prametro del mtodo format(), mis sujos. Por eso {0[0]} se reere al primer a e elemento de la lista que est denida como el primer parmetro del mtodo e a e format(): KB. Mientras que {0[1]} se reere al segundo elemento de la misma

104

CAP ITULO 4. CADENAS DE TEXTO lista: MB. Todo lo que est fuera de las llaves incluido el 1000, los signos a de igual, y los espacios quedan sin tocar. El resultado nal es: 1000KB = 1MB. El {0} se reemplaza por el primer parmetro, {1} se reemplaza por el sea gundo.

Lo que este ejemplo te ensea es que los especicadores de formato pueden n utilizarse para acceder a los elementos y propiedades de las estructuras de datos utilizando (casi) sintaxis de Python. Esto se denomina nombres de campos compuestos. Estos son los nombres de campos que funcionan: Pasar una lista y acceder a un elemento de la lista utilizando un ndice (como en el ejemplo anterior). Pasar un diccionario y acceder a los valores del mismo utilizando una clave. Pasar un mdulo y acceder a sus variables y funciones por nombre. o Pasar una instancia de clase y acceder a sus propiedades y mtodos por nome bre. Cualquier combinacin de las anteriores. o Solamente para despejar tu cerebro te muestro aqu un ejemplo que combina todo lo anterior.
1 >>> import parahumanos 2 >>> import s y s 3 >>> 1MB = 1 0 0 0 { 0 . modules [ parahumanos ] . SUFIJOS [ 1 0 0 0 ] [ 0 ] } . format ( s y s ) 4 1MB = 1000KB

As es como funciona: El mdulo sys almacena informacin sobre la instancia del lenguaje Python o o que se est ejecutando en ese momento. Puesto que lo has importado, puedes a pasar el propio mdulo sys como parmetro del mtodo format(). As que el o a e campo de sustitucin {0} se reere al mdulo sys. o o sys.modules es un diccionario que almacena todos los mdulos que se han imo portando en la instancia de Python que se est ejecutando. Las claves son los a nombres de los mdulos en formato cadena de texto; los valores son propios o mdulos (objetos mdulo). Por ello {0.modules} se reere al diccionario que o o contiene todos mdulos que se han importado en Python hasta este momento. o

4.4. FORMATEAR CADENAS

105

sys.modules[parahumanos] retorna el objeto parahumanos que acabas de importar. El campo de reemplazo sys.modules[parahumanos] se reere al mdulo o parahumanos. Observa que existe una ligera diferencia de sintaxis. En el cdigo o de Python, las claves del diccionario es de tipo cadena; para referirse a ellas, es necesario poner comillas alrededor del nombre del mdulo (parahumanos). o Pero dentro de los campos de sustitucin no se ponen las comillas alrededor o del nombre de la clave del diccionario (parahumanos). Segn el PEP 3101: u Formateo avanzado de cadenas: Las reglas para el parseo de la clave de un campo de sustitucin son muy simples, si comienza por un d o gito, se trata como numrica, en caso contrario se interpreta como una cadena. e sys.modules[parahumanos].SUFIJOS es el diccionario denido en el mdulo pao rahumanos. El campo de sustitucin sys.modules[parahumanos].SUFIJOS se reo ere a este diccionario. sys.modules[parahumanos].SUFIJOS[1000] es la lista de sujos mltiplos de u 1000: [KB, MB, GB, TB, PB, EB, ZB, YB] Por lo que el campo de sustitucin sys.modules[parahumanos].SUFIJOS[1000] se reere a esta lista. o sys.modules[parahumanos].SUFIJOS[1000][0] es el primer elemento de la lista de sujos: [KB]. Por lo tanto el campo de sustitucin sys.modules[parahumanos].SUFIJOS[1000][0] o se reere a la cadena KB.

4.4.2.

Especicaciones de formato

Pero espera! Hay mas! Vamos a echar otro vistazo a la l nea de cdigo ms o a extraa de parahumanos.py: n
1 2 i f tamanyo < m u l t i p l o : return { 0 : . 1 f } {1} . format ( tamanyo , s u f i j o )

Como ya sabemos, {1} se sustituye por el segundo parmetro sujo del mtodo a e format(). Pero qu es {0:.1f}? Tiene dos partes, {0} que ya conoces, y :.f que no coe noces. La segunda parte (desde los dos puntos hasta la letra f) dene el especicador de forma que ana cmo debe sustituirse la variable al formatearla. o Los especicadores de formato te permiten indicar cmo se debe efeco tuar la sustitucin del texto, como sucede con la funcin printf() en el o o lenguaje C. Puedes aadir ceros o espacios de relleno delante del nmero, n u alinear cadenas, controlar la precisin de decimales o convertir el nmero o u a hexadecimal.

106

CAP ITULO 4. CADENAS DE TEXTO

Dentro del campo de sustitucin se utiliza el s o mbolo de dos puntos (:) para marcar el comienzo del especicador de formato. El especicador de formato .1 signica que se redondee a la dcima ms prxima (que se muestre unicamente e a o un d gito despus del punto decimal). El especicador f indica que el nmero e u debe mostrarse en formato punto jo (por oposicin a la notacin exponencial u o o otra representacin de un nmero). Si la variable tamanyo vale 698.24 y la variable o u sujo vale GB la cadena formateada resultante es 698.2 GB, porque 698.24 se redondea con un solo d gito despus del punto decimal. e
1 >>> { 0 : . 1 f } {1} . format ( 6 9 8 . 2 4 , GB ) 2 6 9 8 . 2 GB

Para conocer los detalles exactos de los especicadores de formato puedes consultar el apartado Mini-lenguaje de especicacin de formato5 de la documentacin o o ocial de Python 3.

4.5.

Otros mtodos habituales de manipulacin e o de cadenas

Adems de formatearlas, es posible hacer muchas otras cosas de utilidad con a las cadenas de texto.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 >>> s = Los a r c h i v o s t e r m i n a d o s son e l re . . . s u l t a d o de a o s de e s t u d i o c i e n t n . . . f i c o combinados con l a . . . e x p e r i e n c i a de a o s . n >>> s . s p l i t l i n e s ( ) [ Los a r c h i v o s t e r m i n a d o s son e l re , s u l t a d o de a o s de e s t u d i o c i e n t , n f i c o combinados con l a , e x p e r i e n c i a de a o s . ] n >>> print ( s . l o w e r ( ) ) l o s a r c h i v o s t e r m i n a d o s son e l re s u l t a d o de a o s de e s t u d i o c i e n t n f i c o combinados con l a e x p e r i e n c i a de a o s . n >>> s . l o w e r ( ) . count ( l ) 4

1. Lnea 1: Puedes introducir cadenas multil nea en la consola interactiva de Python. Cuando inicias una cadena de texto multil nea debes pulsar la tecla
5

http://docs.python.org/3.1/library/string.html#format-specication-mini-language

4.5. OTROS METODOS HABITUALES DE MANIPULACION DE CADENAS107 INTRO para continuar en la siguiente l nea. Al teclear las triples comillas del nal, se cierra la cadena de texto y el siguiente INTRO que pulses ejecutar la a sentencia (en este caso asignar la cadena a la variable s). a 2. Lnea 5: El mtodo splitlines() toma una cadena multil e nea y devuelve una lista de cadenas de texto, una por cada l nea que contuviese la cadena original. Observa que las l neas no incluyen los retornos de carro o nales de l nea que tuviese la cadena original. 3. Lnea 10: El mtodo lower() convierte toda la cadena de texto a minsculas e u (El mtodo upper() convertir toda la cadena de texto a maysculas). e a u 4. Lnea 15: El mtodo count() cuenta el nmero de veces que aparece una sub e u cadena en la cadena de texto. S Hay 4 caracteres l en la cadena. ! Pongamos un caso muy comn. Supn que tienes una cadena de texto en forma u o de parejas clave-valor, clave1=valor1&clave2=valor2, y quieres dividirla y crear un diccionario de la forma {clave1: valor1, clave2: valor2}.
1 2 3 4 5 6 7 8 9 10 >>> c o n s u l t a = u s u a r i o=p i l g r i m&b a s e d e d a t o s=master&c l a v e=PapayaWhip >>> u n a l i s t a = c o n s u l t a . s p l i t ( & ) >>> u n a l i s t a [ u s u a r i o=p i l g r i m , b a s e d e d a t o s=master , c l a v e=PapayaWhip ] >>> u n a l i s t a d e l i s t a s = [ v . s p l i t ( = , 1 ) f o r v in u n a l i s t a ] >>> u n a l i s t a d e l i s t a s [ [ u s u a r i o , p i l g r i m ] , [ b a s e d e d a t o s , master ] , [ c l a v e , PapayaWhip ] ] >>> a d i c t = d i c t ( u n a l i s t a d e l i s t a s ) >>> a d i c t { c l a v e : PapayaWhip , u s u a r i o : p i l g r i m , b a s e d e d a t o s : master }

1. Lnea 2: El mtodo split() toma un parmetro, un delimitador, y divide la e a cadena en una lista de cadenas basndose en el delimitador proporcionado. En a este ejemplo, el delimitador es el carcter &. a 2. Lnea 5: Ahora tenemos una lista de cadenas, cada una de ellas con una clave seguida del s mbolo = y de un valor. Podemos utilizar las listas por comprensin para iterar sobre esta lista y dividir cada una de estas cadenas de texto o en dos cadenas utilizando el mtodo split pasndole un segundo parmetro que e a a indica que unicamente utilice la primera ocurrencia del carcter separador (En a teor una cadena podr tener ms de un s a a a mbolo igual si el valor, a su vez, contiene tambin el s e mbolo igual, por ejemplo: clave=valor=cero, con lo que clave=valor=cero.split(=) dar como resultado [clave, valor, cero]). a 3. Lnea 8: Finalmente, Python puede convertir esa lista de listas en un diccio nario con solo pasarla como parmetro a la funcin dict(). a o

108

CAP ITULO 4. CADENAS DE TEXTO El ejemplo anterior, explica un caso que se parece a lo que habr que a hacer para reconocer los parmetros de una URL. pero en la vida real, el a reconocimiento de los parmetros de una URL es ms complejo. Si vas a a a tener que reconocer los parmetros que recibes mediante una URL utiliza a la funcin de la librer urlib.parse denominada parse qs()6 , que reconoce o a los casos ms complejos. a

4.5.1.

Troceado de cadenas

Cuando ya has denido una cadena puedes recuperar cualquier parte de ella creando una nueva cadena de texto. A esto se denomina troceado/particionado de cadenas7 . Esto funciona de forma idntica a como funciona para las listas, lo que e tiene sentido, porque las cadenas de texto no son ms que cadenas de caracteres. a
1 2 3 4 5 6 7 8 9 10 11 >>> una cadena = Mi v o c a b u l a r i o comienza donde e l tuyo t e r m i n a >>> una cadena [ 3 : 1 4 ] vocabulario >>> una cadena [ 3 : 3 ] v o c a b u l a r i o comienza donde e l tuyo term >>> una cadena [ 0 : 2 ] Mi >>> una cadena [ : 2 3 ] Mi v o c a b u l a r i o comienza >>> una cadena [ 2 3 : ] donde e l tuyo t e r m i n a

1. Lnea 2: Puedes recuperar una parte de la cadena de texto, una parte de ella, especicando dos ndices. El valor de retorno es una nueva cadena que comienza en el primer ndice y termina en el elemento anterior al segundo ndice. 2. Lnea 4: Como sucede con las listas, puedes utilizar ndices negativos para seleccionar. 3. Lnea 6: Las cadenas tambin comienzan a contar en cero, por lo que una cadena[0:2] e devuelve los dos primeros elementos de la cadena, comenzando en la posicin o una cadena[0] hasta la posicin una cadena[2], pero sin incluirla. o 4. Lnea 8: Si el ndice de la parte izquierda vale 0 puedes omitirlo. De este modo, una cadena[:23] es lo mismo que una cadena[0:18]. Ya que en ausencia del primer ndice se asume el nmero 0. u
6 7

http://docs.python.org/3.1/library/urllib.parse.html#urllib.parse.parse qs Nota del traductor: slicing en ingls e

4.6. CADENAS DE TEXTO Y BYTES

109

5. Lnea 10: De forma similar, si el ndice de la parte derecha de la cadena coincide con la longitud de la cadena, puedes omitirlo. As que una cadena[23:] es lo mismo que una cadena[23:45] al medir esta cadena 45 caracteres. Como ves, existe una estupenda simetr en esto, en esta cadena de 45 caracteres a una cadena[0:23] devuelve los primeros 23 caracteres, y una cadena[23:] devuelve todo lo dems, salvo los 23 caracteres iniciales. De hecho una cadena[:n] a siempre retornar los primeros n caracteres, y una cadena[n:] retornar el resto, a a independientemente de la longitud que tenga la cadena.

4.6.

Cadenas de texto y Bytes

Los bytes son bytes; los caracteres son una abstraccin. A una secuencia ino mutable de caracteres Unicode se le llama cadena de texto. Una secuencia inmutable de nmeros entre el 0 y el 255 es un objeto que se denomina bytes. u
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 >>> by = b abcd \ x65 >>> by b abcde >>> type ( by ) <c l a s s b y t e s > >>> l e n ( by ) 5 >>> by += b \ x f f >>> by b abcde \ x f f >>> l e n ( by ) 6 >>> by [ 0 ] 97 >>> by [ 0 ] = 102 Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> TypeError : b y t e s o b j e c t d o e s not s u p p o r t item a s s i g n m e n t

1. Lnea 1: Para denir un objeto bytes se usa la sintaxis de literal de bytes que es b. Cada byte dentro del literal de bytes se interpreta como un carcter a ASCII o un carcter codicado en nmero hexadecimal desde a u x00 a xFF (0-255). 2. Lnea 4: El tipo de un objeto bytes es bytes. 3. Lnea 6: Como sucede con las listas y cadenas, puedes conocer la longitud de un objeto bytes utilizando la funcin interna len(). o

110

CAP ITULO 4. CADENAS DE TEXTO

4. Lnea 8: Como sucede con las listas y cadenas, puedes utilizar el operador + para concatenar objetos bytes. El resultado es un nuevo objeto bytes. 5. Lnea 11: Concatenar un objeto bytes de 5 bytes con uno de 1 byte da como resultado un objeto bytes de 6 bytes. 6. Lnea 13: Como sucede con las listas y cadenas, puedes utilizar la notacin o de ndices para obtener bytes individuales del objeto bytes. Los elementos individuales de una cadena son de tipo cadena; los elementos individuales de un objeto bytes son nmeros enteros. Espec u camente, enteros entre 0 y 255. 7. Lnea 15: Un objeto bytes es inmutable; no puedes asignar bytes individua les. Si necesitas modicar bytes individuales de un objeto bytes, es necesario particionar y concatener para crear un nuevo objeto bytes que contenga los elementos deseados. La alternativa es convertir el objeto bytes en un bytearray que s permite modicacin. o
1 2 3 4 5 6 7 8 9 >>> by = b abcd \ x65 >>> b a r r = b y t e a r r a y ( by ) >>> b a r r b y t e a r r a y ( b abcde ) >>> l e n ( b a r r ) 5 >>> b a r r [ 0 ] = 102 >>> b a r r bytearray (b fbcde )

1. Lnea 2: Para convertir un objeto bytes en un objeto modicable de tipo by tearray puedes utilizar la funcin interna bytearray(). o 2. Lnea 5: Todos los mtodos y operaciones que existen en el objeto bytes tam e bin estn disponibles en el objeto bytearray. e a 3. Lnea 7: Una de las diferencias es que al objeto bytearray es posible modi carle bytes individuales utilizando la notacin de o ndice. El valor que se puede asignar debe estar entre 0 y 255. Algo que no se puede hacer es mezclar bytes y cadenas.

4.6. CADENAS DE TEXTO Y BYTES


1 2 3 4 5 6 7 8 9 10 11 12 >>> by = b d >>> s = abcde >>> by + s Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> TypeError : can t c o n c a t b y t e s t o s t r >>> s . count ( by ) Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n >, l i n e 1 , i n <module> TypeError : Can t c o n v e r t b y t e s o b j e c t t o s t r i m p l i c i t l y >>> s . count ( by . decode ( a s c i i ) ) 1

111

1. Lnea 3: No puedes concatenar bytes y cadenas. Son dos tipos de dato dife rentes. 2. Lnea 7: No puedes contar las veces que aparece una secuencia de bytes en una cadena, porque no existen bytes en una cadena. Una cadena es una secuencia de caracteres. Tal vez lo que quer contar era las veces que aparece la cadena que as obtendr despus de haber decodicado la secuencia de bytes interpretndola as e a a partir de una tabla de codicacin de caracteres particular. Si es as debes o , decirlo expl citamente. Python 3 no convertir implic a tamente bytes en cadenas o cadenas en bytes. 3. Lnea 11: Por una sorprendente coincidencia esta l nea de cdigo dice cuenta o las ocurrencias de la cadena que se obtiene despus de decodicar la secuencia e de bytes en esta tabla de caracteres particular (ASCII). Y de este modo has llegado a la relacin que existe entre las cadenas de texto o y los bytes: los objetos bytes tienen un mtodo decode() que toma como parmetro e a una tabla de codicacin de caracteres y retorna una cadena. Y las cadenas de o texto tienen un mtodo denominado encode() que toma una tabla de codicacin e o de caracteres y retorna un objeto bytes. En el ejemplo anterior, la decodicacin fue o relativamente directa convertir una secuencia de bytes que estaba en la codicacin o de caracteres ASCII en una cadena de texto. Pero el mismo proceso funciona con cualquier tabla de codicacin de caracteres siempre que dicha tabla soporte los o caracteres existentes en la cadena incluso con codicaciones heredadas (previas a Unicode).

112
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

CAP ITULO 4. CADENAS DE TEXTO

>>> s = Python >>> l e n ( s ) 9 >>> by = s . encode ( u t f 8 ) >>> by b \ xe5 \ x b f \ xab \ xe4 \ xb9 \ x90 Python >>> l e n ( by ) 13 >>> by = s . encode ( gb18030 ) >>> by b \ x b f \ xec \ xc0 \ xd6 Python >>> l e n ( by ) 11 >>> by = s . encode ( u t f 16 ) >>> by b \ x f f \ x f e \xeb PN \x00P\ x00y \ x00t \ x00h \ x00o \ x00n \ x00 >>> l e n ( by ) 20 >>> v u e l t a = by . decode ( u t f 16 ) Python >>> v u e l t a == s True

1. Lnea 1: Esto es una cadena de texto, tiene 9 caracteres. 2. Lnea 4: El resultado de codicar la cadena en UTF-8 es un objeto bytes. Tiene 13 bytes. 3. Lnea 9: El resultado de codicar la cadena en GB18030 es un objeto bytes de 11 bytes. 4. Lnea 14: El resultado de codicar la cadena en UTF-16 es un objeto bytes de 20 bytes. 5. Lnea 19: Al decodicar el objeto bytes utilizando la codicacin adecuada (la o misma que se us al codicarlo) se obtiene una cadena de texto. En este caso o tiene 9 caracteres. Como se puede ver, es una cadena idntica a la original. e

4.7.

Postdata: Codicacin de caracteres del cdio o go fuente de Python

Python 3 asume que el cdigo fuente cada chero .py est codicado en o a UTF-8.

4.8. LECTURAS RECOMENDADAS En Python 2, la codicacin de caracteres por defecto de los cheros o .py era ASCII. En Python 3, la codicacin por defecto de los cheros es o UTF-8

113

Si quisieras utilizar una codicacin de caracteres diferente en el chero con o el cdigo fuente, puedes incluir una declaracin de codicacin de caracteres en la o o o primera l nea cada chero. La siguiente declaracin dene que el chero se encuentra o en una codicacin windows-1252 o
1 #

c o d i n g : windows 1252

Tcnicamente la indicacin de la codicacin de caracteres puede estar en la e o o segunda l nea si la primera l nea es una declaracin de lanzador de ejecucin del o o estilo de UNIX.
1 #! / u s r / b i n / python3 2 # c o d i n g : windows 1252

Para disponer de ms informacin consulta la propuesta de mejora de Python a o 8 PEP 263 .

4.8.

Lecturas recomendadas

Sobre Unicode en Python: Unicode en Python - http://docs.python.org/3.0/howto/unicode.html Qu hay nuevo en Python 3: Texto y datos en lugar de Unicode y 8-bits e http://docs.python.org/3.0/whatsnew/3.0.html#text-vs-data-instead-of-unicodevs-8-bit Sobre Unicode en general: El m nimo absoluto que todo programador debe conocer positiva y absolutamente sobre Unicode y codicacin de caracteres (Sin excusas!): o http://www.joelonsoftware.com/articles/Unicode.html Sobre las bondades de Unicode: http://www.tbray.org/ongoing/When/200x/2003/04/06/Unicode
8

http://www.python.org/dev/peps/pep-0263/

114

CAP ITULO 4. CADENAS DE TEXTO Sobre cadenas de caracteres: http://www.tbray.org/ongoing/When/200x/2003/04/13/Strings Caracteres y bytes: http://www.tbray.org/ongoing/When/200x/2003/04/26/UTF Sobre codicacin de caracteres en otros formatos: o Codicacin de caracteres en XML: o http://feedparser.org/docs/character-encoding.html Codicacin de caracteres en HTML: o http://blog.whatwg.org/the-road-to-html-5-character-encoding Sobre cadenas y formateo de cadenas: stringOperaciones comunes sobre cadenas: http://docs.python.org/3.1/library/string.html Sintaxis de formateo de cadenas de texto: http://docs.python.org/3.1/library/string.html#formatstrings Especicacin del minilenguaje de formato: o http://docs.python.org/3.1/library/string.html#format-specication-mini-language PEP 3101: Formateo avanzado de cadenas: http://www.python.org/dev/peps/pep-3101/

Cap tulo 5 Expresiones regulares


Nivel de dicultad:   Algunas personas, cuando se enfrentan a un problema, piensan: Ya s! usar expresiones regulares. Y as, acaban e e enfrentndose a dos problemas. a Jamie Zawinski

5.1.

Inmersin o

Todo lenguaje de programacin moderno dispone de funciones internas para o trabajar con cadenas. En Python las cadenas de texto tienen mtodos para buscar e y reemplazar: index(), nd(), split(), count(), replace(), etc. Pero esos mtodos estn e a limitados a los casos ms simples. Por ejemplo, el mtodo index() busca por una a e unica subcadena, y la bsqueda siempre distingue entre maysculas y minsculas. u u u Para poder hacer bsquedas que no distingan entre ellas debes utilizar s.lower() o u s.upper() y asegurarte de que tus cadenas de bsqueda se encuentran en el mismo u caso. Los mtodos replace() y split(). e Si tu objetivo se cumple con estos mtodos deber usarlos. Son rpidos, sime as a ples y sencillos de leer; y hay mucho que decir a favor del cdigo legible, simple o y rpido. Pero si te descubres escribiendo un montn de funciones para manipular a o cadenas con sentencias if para contemplar casos especiales, o te encuentras encadenando llamadas a split() y join() para trocear tus cadenas de texto, puede que necesites utilizar expresiones regulares. Las expresiones regualres son una forma poderosa y (en su mayor parte) estndar de bsqueda, reemplazo y anlisis de texto con patrones de caracteres a u a 115

116

CAP ITULO 5. EXPRESIONES REGULARES

complejos. Aunque la sintaxis de las expresiones regulares es compacta y muy diferente del cdigo normal, el resultado puede resultar ser ms legible que una solucin o a o manual que utilice un montn de funciones de cadenas de texto encadenadas. Incluso o existe un modo estndar de incluir comentarios dentro de las expresiones regulares, a por lo que puedes incluir una documentacin detallada dentro de ellas. o Si has utilizado expresiones regulares en otros lenguajes (como Perl, JavaScript o PHP), la sintaxis de Python te ser muy familiar. Puedes a limitarte a leer las funciones disponibles y sus parmetros en el resumen a de la documentacin del mdulo re1 o o

5.2.

Caso de estudio: direcciones de calles

Esta serie de ejemplos se inspira en un problema de la vida real que tuve en en el trabajo hace varios aos, cuando necesit depurar y estandarizar una lista de n e direcciones postales exportadas de un sistema heredado antes de importarlas en un nuevo sistema2 . Este ejemplo muestra la forma en la que abord el problema: e
1 2 3 4 5 6 7 8 9 10 11 >> s = 100 NORTH MAIN ROAD >>> s . r e p l a c e ( ROAD , RD. ) 100 NORTH MAIN RD. >>> s = 100 NORTH BROAD ROAD >>> s . r e p l a c e ( ROAD , RD. ) 100 NORTH BRD. RD. >>> s [ : 4 ] + s [ 4 : ] . r e p l a c e ( ROAD , RD. ) 100 NORTH BROAD RD. >>> import r e >>> r e . sub ( ROAD$ , RD. , s ) 100 NORTH BROAD RD.

1. Lnea 2: Mi objetivo es estandarizar las direcciones postales de forma que ROAD siempre se escribiera como RD.. En un primer vistazo pens que era e lo sucientemente simple como para que pudiera utilizar el mtodo replace(). e Despus de todo, las cadenas de texto estaban en maysculas por lo que no e u ser un problema la existencia de posibles minsculas. Y la cadena de bsquea u u da, ROAD, era una constante. Y en este simple ejemplo s.replace(), de hecho, funciona. 2. Lnea 5: La vida, desafortunadamente, est llena de contraejemplos, y rpida a a mente encontr este caso. El problema aqu es que ROAD aparece dos veces e
1 2

http://docs.python.org/dev/library/re.html#module-contents Como ves no me invento cosas de la nada, los ejemplos son realmente utiles.

5.2. CASO DE ESTUDIO: DIRECCIONES DE CALLES

117

en la direccin, una de ellas siendo parte del nombre de la calle BROAD y o otra por s misma. El mtodo replace() encuentra ambos casos y los reemplaza e ciegamente; destruyendo las direcciones. 3. Lnea 7: Para resolver el problema de las direcciones con ms de una ocurren a cia de la cadena de texto ROAD puedes recurrir a algo como esto: unicamente buscar y reemplazar ROAD en los ultimos cuatro caracteres de la direccin o s[-4:], y dejar el resto de la cadena igual, s[:-4]. Como ves, se est volviendo ina manejable. Por ejemplo, la forma la solucin depende del tamao de la cadena o n de bsqueda. Si intentases sustituir STREET por ST., necesitar utilizar u as s[:-6] y s[-6:].replace(...). Te gustar volver dentro de seis meses a depurar a este cdigo? S que yo no. o e 4. Lnea 9: Es el momento de pasar a las expresiones regulares. En Python esta funcionalidad se incluye en el mdulo re. o 5. Lnea 10: Echa un vistazo al primer parmetro: ROAD$. Esta simple expre a sin regular unicamente encuentra los casos en los que ROAD aparece al nal o de la l nea. El s mbolo $ signica n de la cadena. Exite otro carcter, el a circunejo: , que signica inicio de la cadena . Mediante el uso de la funcin o re.sub(), se busca en la cadena s la existencia de la expresin regular ROAD$ o para sustituirla por RD.. Esto permite encontrar ROAD al nal de la cadena s, pero no la parte contenida en BROAD puesto que se encuentra en medio de la cadena s. Continuando con mi relato sobre la depuracin de las direcciones postales, pronto deso ncuentra el comienzo de una e cubr que el ejemplo anterior, encontrar ROAD cadena. $ encuentra el nal. al nal de la direccin, no era suciente; no todas o las direcciones inclu la destinacin de la calle. an o Algunas direcciones simplemente terminaban con el nombre de la calle. La mayor parte de las veces no pasaba nada, pero si el nombre de la calle era BROAD la expresin regular encontraba ROAD al nal de la cadena como parte de la palabra, o que no era lo que quer yo. a

118
1 2 3 4 5 6 7 8 9 10 11 12

CAP ITULO 5. EXPRESIONES REGULARES

>>> s = 100 BROAD >>> r e . sub ( ROAD$ , RD. , s ) 100 BRD. >>> r e . sub ( \bROAD$ , RD. , s ) 100 BROAD >>> r e . sub ( r \bROAD$ , RD. , s ) 100 BROAD >>> s = 100 BROAD ROAD APT. 3 >>> r e . sub ( r \bROAD$ , RD. , s ) 100 BROAD ROAD APT. 3 >>> r e . sub ( r \bROAD\b , RD. , s ) 100 BROAD RD. APT 3

1. Lnea 4: Lo que yo realmente quera era buscar ROAD cuando estuviera al nal de la l nea y fuese una palabra por s misma (y no parte de una palabra mayor). Para expresar esto en una expresin regular debes utilizar zb, que o indica que un l mite de palabra debe existir en ese lugar. En Python, expresar esta cadena es algo complicado debido a que el s mbolo z suele indicar un carcter de escape, y hay que escribirlo dos veces para representarlo como tal. a Esto hay quien lo llama la plaga de las barras inclinadas invertidas, y es el argumento para decir que las expresiones regulares son ms sencillas en Perl a que en Python. En el lado negativo, Perl mezcla la sintaxis de las expresiones regulares con otra sintaxis, por lo que si tienes un error, es ms dif saber si a cil el error es en la sintaxis o en la expresin regular. o 2. Lnea 6: Para evitar la plaga de las barras inclinadas invertidas puedes utili zar lo que se llaman cadenas de texto crudas3 mediante el uso del prejo r delante de la cadena de texto. Este tipo de cadena de texto le dice a Python que nada de lo que contiene es un carcter de escape: la cadena zt represena ta al carcter tabulador, pero rzt es una cadena que contiene como primer a carcter la barrra inclinada invertida seguida de la letra t. Por eso, recomiena do que siempre utilices cadenas de texto crudas cuando vayas a escribir una expresin regular; en caso contrario, las cosas se vuelven confusas en cuanto la o expresin regular es algo compleja (y las expresiones regulares ya confunden o sucientemente por s mismas). 3. Lnea 9: Desafortunadamente rpidamente encontr ms casos que contradi a e a jeron mi razonamiento. En este caso la direccin conten la palabra ROAD o a pero no al nal de la cadena de texto, ya que conten tambin el nmero del a e u apartamento despus de la designacin de la calle. Al no encontrarse al nal e o de la cadena, no se sustituye nada porque la expresin regular no coincide. o
3

Nota del traductor: raw en ingls. e

5.3. CASO DE ESTUDIO: NUMEROS ROMANOS

119

4. Lnea 11: Para resolver este problema acab quitando el carcter $ y poniendo e a otro zb. De esta forma la expresin regular signica encuentra ROAD cuando o es una palabra completa en cualquier parte de la cadena, tanto si est al a principio, al nal o en cualquier otra parte.

5.3.

Caso de estudio: n meros romanos u

Es muy probable que hayas visto nmeros romanos en alguna parte incluso u aunque no los hayas reconocido. Puedes haberlos visto en los crdidos de las pel e culas antiguas o programas de televisin (Copyright MCMXLVI) o en las paredes o de las bibliotecas y universidades (Establecido en MDCCCLXXXVIII en lugar de establecido en 1888). Puede que incluso los hayas visto en referencias bibliogra cas. Es un sistema de representacin numrica que se remonta a la poca del imperio o e e romano (de ah el nombre). En los nmeros romanos existen siete caracteres que se repiten y combinan de u diferentes formas para representar nmeros: u I=1 V=5 X = 10 L = 50 C = 100 D = 500 M = 1000 Existen una reglas generales para construir nmeros romanos: u Los caracteres son aditivos, I es 1, II es 2 y III es 3. VI es 6 (literalmente 5 + 1), VII es 7 (5+1+1) y XVIII es 18 (10+5+1+1+1). Los caracteres que representan unidades, decenas, centenas y unidades de millar (I, X, C y M) pueden aparecer juntos hasta tres veces como mximo. Para a el 4 debes restar del carcter V, L D (cinco, cincuenta, quinientos) que se a o encuentre ms prximo a la derecha. No se puede representar el cuatro como a o IIII, en su lugar hay que poner IV (5-1). El nmero 40 se representa como XL u (10 menos que 50: 50-10). 41 = XLI, 42 = XLII, 43 = XLIII y luego 44 = XLIV (diez menos que cincuenta ms uno menos que cinco: 50-10+5-1). a

120

CAP ITULO 5. EXPRESIONES REGULARES De forma similar, para el nmero 9, debes restar del nmero siguiente ms u u a prximo que represente unidades, decenas, centenas o unidades de millar (I, o X, C y M). 8 = VIII, pero 9 = IX (1 menos que 10), no 9 = VIIII puesto que el carcter I no puede repetirse cuatro veces seguidas. El nmero 90 se representa a u con XC y el 900 con CM. Los caracteres V, L y D no pueden repetirse; el nmero 10 siempre se representa u como X y no como VV. El nmero 100 siempre se representa como C y nunca u como LL. Los nmeros romanos siempre se escriben de los caracteres que representan u valores mayores a los menores y se leen de izquierda a derecha por lo que el orden de los caracteres importa mucho. {DC es el nmero 600; CD otro nmero, u u el 400 (500 - 100). CI es 101, mientras que IC no es un nmero romano vlido u a 4 porque no puedes restar I del C .

5.3.1.
.

A b squeda de coincidencias de las unidades de millar u

Qu costar conocer que una cadena de texto es un nmero romano vlido? e a u a Vamos a hacerlo d gito a d gito para facilitar la tarea y la explicacin. Puesto que o los nmeros romanos siempre se escriben del mayor al menor, vamos a comenzar u por el mayor: las unidades de millar. Para los nmeros 1000 y superiores los miles u se representan por una serie de caracteres M.
1 2 3 4 5 6 7 8 9 10 11 >>> import r e >>> p a t t e r n = M?M?M? $ >>> r e . s e a r c h ( p a t t e r n , M ) <SRE Match o b j e c t a t 0106FB58> >>> r e . s e a r c h ( p a t t e r n , M ) M <SRE Match o b j e c t a t 0106 C290> >>> r e . s e a r c h ( p a t t e r n , M M ) M <SRE Match o b j e c t a t 0106AA38> >>> r e . s e a r c h ( p a t t e r n , M M ) M M >>> r e . s e a r c h ( p a t t e r n , ) <SRE Match o b j e c t a t 0106F4A8>

1. Lnea 2: Este patrn tiene tres partes: identica el comienzo de la l o nea unicamente. Si no lo indicramos as el resto del patrn validar cualquier a , o a posicin dentro de una cadena en la que se encontrase, cosa que no quieres. Lo o que quieres es estar seguro de que los caracteres M se encuentran al comienzo
4

Para representar el 99 deber usar: XCIL (100 - 10 + 10 - 1) as

5.3. CASO DE ESTUDIO: NUMEROS ROMANOS

121

de la cadena. M? indica que se valide si existe un carcter M de forma opcional a en la posicin indicada. Como se repite tres veces, lo que ests diciendo es que o a se valide si existe el carcter M de cero a tres veces (al principio de la cadena a debido al ). Y por ultimo, $ valida el nal de la l nea. Cuando se combina con el carcter al comienzo, signica que el patrn debe coincidir con la cadena a o de texto completa, por lo que en este caso unicamente es posible que tenga de cero a tres caracteres M. 2. Lnea 3: La esencia del mdulo re es la funcin search(), que toma como pri o o mer parmetro una expresin regular (pattern) y como segundo una cadena a o de texto que es la que se comprobar para ver si coincide con la expresin a o regular. Si se encuentra una cadena identicada por las expresin regular, esta o funcin retorna un objeto que tiene diversos mtodos para describir la cadena o e encontrada. Si no se encuentra nada equivalente al patrn, la funcin retorna o o None. Por ahora lo unico que te interesa es conocer si una cadena cumple el patrn, y para ello basta ver qu valor retorna la funcin search. M cumple la o e o expresin regular, porque el primer M opcional coincide con el primer caracter o de la cadena y las siguientes M del patrn son ignoradas. o 3. Lnea 5: MM cumple la expresin regular porque el primer y segundo M o opcional coinciden con la primera y segunda letra M de la cadena. La tercera M opcional se ignora. 4. Lnea 7: MMM cumple la expresin regular porque los tres caracteres M o coinciden. 5. Lnea 9: MMMM no cumple la expresin regular. Los tres primeros caracteres o coinciden, pero en este momento la expresin regular insiste en que se debe o terminar la cadena (debido al carcter $), pero la cadena de texto no ha a nalizado an, existe una cuarta M. Por eso la funcin search() retorna None. u o 6. Lnea 10: La cadena de textos vac tambin cumple el patrn puesto que los a e o tres caracteres M son opcionales. ? hace que un patrn sea opcional. o

5.3.2.

A la b squeda de coincidencias de las centenas u

Las centenas son ms dif a ciles que los miles, porque existen varias formas exclusivas de representacin dependiendo del valor. o 100 = C

122 200 = CC 300 = CCC 400 = CD 500 = D 600 = DC 700 = DCC 800 = DCCC 900 = CM

CAP ITULO 5. EXPRESIONES REGULARES

Por lo que existen cuatro patrones posibles: CM CD De cero a tres caracteres C (Cero si el lugar de las centenas vale cero). D, seguido de cero a tres caracteres C. Los dos ultimos patrones se pueden combinar: Una D opcional, seguida de cero a tres caracteres C. Este ejemplo muestra cmo validar las centenas de un nmero romano. o u
1 2 3 4 5 6 7 8 9 10 11 >>> import r e >>> p a t t e r n = M?M?M? (CM|CD|D?C?C?C? ) $ >>> r e . s e a r c h ( p a t t e r n , MCM ) <SRE Match o b j e c t a t 01070390 > >>> r e . s e a r c h ( p a t t e r n , MD ) <SRE Match o b j e c t a t 01073A50> >>> r e . s e a r c h ( p a t t e r n , MMMCCC ) <SRE Match o b j e c t a t 010748A8> >>> r e . s e a r c h ( p a t t e r n , MCMC ) >>> r e . s e a r c h ( p a t t e r n , ) <SRE Match o b j e c t a t 01071D98>

5.4. UTILIZACION DE LA SINTAXIS {N,M}

123

1. Lnea 2: El patrn de esta expresin regular comienza igual que el anterior, o o se valida el comienzo de la cadena (), luego de cero a tres unidades de millar (M?M?M?). Luego viene la parte nueva, se denen tres conjuntos de patrones mutuamente excluyentes. Para ello se utilizan los parntesis y la barra vertical: e CM, CD y D?C?C?C? (este ultimo representa a una D opcional seguida de cero a tres C). El analizador de expresiones regulares comprueba cada uno de estos patrones en el orden en que se muestran (de izquierda a derecha), toma el primero que coincide e ignora el resto. Si no coincide ninguno, falla la bsqueda u en la cadena de texto. 2. Lnea 3: MCM cumple el patrn porque la primera M coincide, la segunda y o la tercera del patrn se ignoran, y la CM coincide (los patrones CD y D?C?C?C? o no llegan a considerarse). MCM es la representacin romana del 1900. o 3. Lnea 5: MD cumple porque la primera M coincide, la segunda y tercera M del patrn se ignoran, y el patrn D?C?C?C? coincide en la D (los tres caracteres o o C son opcionales). MD es el nmero romano 1500. u 4. Lnea 7: MMMCCC cumple la expresin regular porque coinciden las tres M o opcionales, y el patrn D?C?C?C? coincide con CCC (la D es opcional y se o ignora). MMMCCC es el nmero romano que representa el 3300. u 5. Lnea 9: MCMC no cumple la expresin. La primera M del patrn coincide, o o las siguientes se ignoran, y CM coincide, pero al nal $ porque espera que se haya acabado la cadena pero an queda la C en la cadena de texto. La C no u coincide como parte del patrn D?C?C?C? porque es mutuamente excluyente o con el patrn CM que es el que se ha cumplido anteriormente. o 6. Lnea 10: La cadena vac an cumple este patrn. puesto que todos los ca a u o racteres M son opcionales y la cadena vac tambin coincide con el patrn a e o D?C?C?C? en el que todos los caracteres son tambin opcionales. e Vaya! Ves qu fcil es que las expresiones regulares se vayan complicando? e a Y por ahora unicamente hemos incluido las unidades de millar y las centenas de los nmeros romanos. Pero si has llegado hasta aqu ahora las decenas y las unidades u , sern fciles para t porque siguen exactamente el mismo patrn. Vamos a ver otra a a , o forma de expresar la expresin regular. o

5.4.

Utilizacin de la sintaxis {n,m} o

En la seccin anterior viste casos de caracteres que se pod repetir hasta tres o an veces. Existe otra forma de representar esto en las expresiones regulares que puede

124

CAP ITULO 5. EXPRESIONES REGULARES

resultar ms legible. Primero observa el mtodo que ya hemos usado en el ejemplo a e anterior.
1 2 3 4 5 6 7 8 9 10 11 12 >>> import r e >>> p a t t e r n = M?M?M? $ >>> r e . s e a r c h ( p a t t e r n , M ) < s r e . SRE Match o b j e c t a t 0x008EE090> >>> p a t t e r n = M?M?M? $ >>> r e . s e a r c h ( p a t t e r n , M ) M < s r e . SRE Match o b j e c t a t 0x008EEB48> >>> p a t t e r n = M?M?M? $ >>> r e . s e a r c h ( p a t t e r n , M M ) M < s r e . SRE Match o b j e c t a t 0x008EE090> >>> r e . s e a r c h ( p a t t e r n , M M ) M M >>>

1. Lnea 3: El patrn coincide con el inicio de la cadena, luego con la primera o M, las restantes se ignoran por ser opcionales, para terminar coincidiendo el $ con el nal de la cadena. 2. Lnea 6: El patrn coincide con el inicio de la cadena, luego coinciden la o primera y segunda M, la tercera se ignora, para terminar encontrando el nal de la cadena. 3. Lnea 9: El patrn coincide con el inicio de la cadena, luego las tres M para o terminar con el nal de la cadena. 4. Lnea 11: El patrn coincide con el inicio de la cadena y las tres M, pero luego o no coincide con el nal de la cadena, que es lo que se espera, puesto que an u queda en la cadena una letra M que falta por coincidir. Por eso el patrn no o se cumple y se devuelve None.
1 2 3 4 5 6 7 8 9 >>> p a t t e r n = M{ 0 , 3 } $ >>> r e . s e a r c h ( p a t t e r n , M ) < s r e . SRE Match o b j e c t a t 0x008EEB48> >>> r e . s e a r c h ( p a t t e r n , M ) M < s r e . SRE Match o b j e c t a t 0x008EE090> >>> r e . s e a r c h ( p a t t e r n , M M ) M < s r e . SRE Match o b j e c t a t 0x008EEDA8> >>> r e . s e a r c h ( p a t t e r n , M M ) M M >>>

1. Lnea 1: Este patrn dice: Busca el comienzo de la cadena, luego busca de cero o a tres M y termina con el nal de la cadena. Los nmeros 0 y 3 del ejemplo u se pueden sustituir por cualquier combinacin; si lo que quisieras fuera que al o menos hubiera una M podr utilizar M{1,3}. as

5.4. UTILIZACION DE LA SINTAXIS {N,M}

125

2. Lnea 2: El patrn coincide con el comienzo de la cadena, luego con la M de o las tres posibles y termina encontrando el nal de la cadena. 3. Lnea 4: El patrn coincide con el comienzo de la cadena, luego con las dos M o de las tres posibles y termina encontrando el nal de la cadena. 4. Lnea 6: El patrn coincide con el comienzo de la cadena, luego con las tres o M y termina encontrando el nal de la cadena. 5. Lnea 8: El patrn coincide con el inicio de la cadena y las tres M, pero luego o no coincide con el nal de la cadena, que es lo que se espera, puesto que an u queda en la cadena una letra M que falta por coincidir. Dicho de otro modo, el patrn espera unicamente un mximo de tres M antes del nal de la cadena o a pero en este caso hay cuatro. Por eso el patrn no se cumple y se devuelve o None. {1,4} busca la coincidencia de una a cuatro veces del patrn relacionado. o

5.4.1.

Comprobacin de las decenas y las unidades o

Vamos a completar la expresin regular de bsqueda de nmeros romanos para o u u incluir a las decenas y unidades. Este ejemplo incluye las decenas.
1 2 3 4 5 6 7 8 9 10 11 >>> p a t t e r n = M?M?M? (CM|CD|D?C?C?C? ) (XC| XL | L?X?X?X? ) $ >>> r e . s e a r c h ( p a t t e r n , MCMXL ) < s r e . SRE Match o b j e c t a t 0x008EEB48> >>> r e . s e a r c h ( p a t t e r n , MCML ) < s r e . SRE Match o b j e c t a t 0x008EEB48> >>> r e . s e a r c h ( p a t t e r n , MCMLX ) < s r e . SRE Match o b j e c t a t 0x008EEB48> >>> r e . s e a r c h ( p a t t e r n , MCMLXXX ) < s r e . SRE Match o b j e c t a t 0x008EEB48> >>> r e . s e a r c h ( p a t t e r n , MCMLXXXX ) >>>

1. Lnea 2: El patrn encuentra el comienzo de la cadena, luego la primera M o opcional, luego CM, luego XL y termina detectando el nal de la cadena. Recuerda que la sintaxis (ABC) signica que se encuentre unicamente una de las tres coincidencias. Al coincidir con XL se ignoran XC y L?X?X?X?. MCMXL es el nmero romano 1940. u 2. Lnea 4: Se encuentra el comienzo de la cadena, luego la primera M opcional, seguido de CM, luego L?X?X?X?. De esta ultima coincide L y se ignoran las

126

CAP ITULO 5. EXPRESIONES REGULARES tres X. Se naliza al encontrar el nal de la cadena. El nmero romano MCML u representa al 1950.

3. Lnea 6: Al mirar el patrn en esta cadena se encuentra el comienzo de la o misma, luego la primera M opcional, seguido de CM, luego L?X?X?X?. Del patrn L?X?X?X? coincide con la L, luego con la primera X opcional y se o ignoran la segunda y tercera X. Se termina encontrando el nal de la cadena. El nmero romano MCMLX es 1960. u 4. Lnea 8: El patrn encuentra el comienzo de la misma, luego la primera M o opcional, seguido de CM, luego L?X?X?X? sirve para identicar LXXX, terminando al encontrar el nal de la cadena. El nmero romano MCMLXXX es u 1980. 5. Lnea 10: Encuentra el comienzo de la cadena, luego la primera M opcional, luego CM, luego la L opcional y las tres X opcionales, despus de este punto e falla la comprobacin, se espera el nal de la cadena pero queda una X. Por o eso, falla toda la expresin regular y se devuelve None. MCMLXXXX no es un o nmero romano vlido. u a (A|B) coincide si la cadena contiene o el patrn A o el B o La expresin para las unidades sigue un patrn idntico. Te ahorrar los detao o e e lles mostrndote el resultado nal. a
1 >>> p a t t e r n = M?M?M? (CM|CD|D?C?C?C? ) (XC| XL | L?X?X?X? ) ( IX | IV |V? I ? I ? I ? ) $

Cmo quedar utilizando la sintaxis {n,m} En el siguiente ejemplo observas o a el resultado.


1 2 3 4 5 6 7 8 9 >>> p a t t e r n = M{ 0 , 3 } (CM|CD|D?C{ 0 , 3 } ) (XC| XL | L?X{ 0 , 3 } ) ( IX | IV |V? I { 0 , 3 } ) $ >>> r e . s e a r c h ( p a t t e r n , MDLV ) < s r e . SRE Match o b j e c t a t 0x008EEB48> >>> r e . s e a r c h ( p a t t e r n , MMDCLXVI ) < s r e . SRE Match o b j e c t a t 0x008EEB48> >>> r e . s e a r c h ( p a t t e r n , MMMDCCCLXXXVIII ) < s r e . SRE Match o b j e c t a t 0x008EEB48> >>> r e . s e a r c h ( p a t t e r n , I ) < s r e . SRE Match o b j e c t a t 0x008EEB48>

1. Lnea 2: Encuentra el comienzo de la cadena, luego una de las tres posibles M, luego D?C{0,3}. De esta ultima subexpresin, coincide con la D y con cero de o las tres posibles C. Lo siguiente que hace es coincidir con L?X{0,3} al encontrar la L y cero de tres X. Luego coincide con V?I{0,3} al encontrar la V y cero de

5.5. EXPRESIONES REGULARES DETALLADAS

127

tres posibles I. Se termina con la coincidencia satisfecha al encontrar el n de la cadena. El nmero romano MDLV representa al 1555. u 2. Lnea 4: Encuentra el comienzo de la cadena, luego dos de las tres posibles M; luego D?C{0,3} encontrando D y una C; luego coincide con L?X{0,3} al encontrar L y una X; luego coincide con V?I{0,3} al encontrar V y una de las tres posibles I, y termina con al nal de la cadena. El nmero romano u MMDCLXVI es el 2666. 3. Lnea 6: Encuentra el comienzo de la cadena, luego las tres posibles M; luego D?C{0,3} encuentra D y tres C; luego coincide con L?X{0,3} al encontrar L y tres X; luego coincide con V?I{0,3} al encontrar V y las tres posibles I, y termina con al nal de la cadena. El nmero romano MMMDCCCLXXXVIII es u el 3888 y es el mayor que se puede escribir sin utilizar una sintaxis extendida. 4. Lnea 8: Observa atentamente (me siento como un mago, mirad atentamente nios, voy a sacar un conejo de mi chistera). En este caso se encuentra el n comienzo de la cadena, luego ninguna M de las tres posibles, luego coincide con D?C{0,3} a saltarse la D opcional y encontrar cero caracteres C, luego coincide con L?X{0,3} por el mismo motivo que antes, y lo mismo sucede con V?I{0,3}. Luego encuentra el nal de la cadena. Fantstico! a Si has seguido todas las explicaciones y las has entendido a la primera ya lo ests haciendo mejor que lo hice yo. Ahora imag a nate tratando de comprender las expresiones regulares que haya escrito otra persona en medio de una funcin cr o tica para tu programa. O piensa simplemente en tener que volver a ver las expresiones regulares que escribiste hace unos meses. Yo lo he tenido que hacer y no es agradable. Vamos a ver ahora una sintaxis alternativa que puede ayudarte que las expresiones regulares sean ms comprensibles. a

5.5.

Expresiones regulares detalladas

Hasta ahora has visto lo que llamar expresiones regulares compactas. Son e dif ciles de leer e incluso, aunque averiges lo que hacen, no puedes estar seguro u de acordarte seis meses despus. En realidad, unicamente necesitas documentarlas. e Para eso puedes utilizar la documentacin incrustada5 . o Python te permite hacerlo mediante el uso de las expresiones regulares detalladas. Se diferencia de una expresin regular compacta en dos cosas: o
5

Nota del Traductor: inline en ingls. e

128

CAP ITULO 5. EXPRESIONES REGULARES Los espacios en blanco se ignoran: espacios, tabuladores y retornos de carro o saltos de l nea. No se tienen en cuenta para el cumplimiento o no de la expresin o regular. Si quieres que exista realmente tal coincidencia ser necesario que a incluyas el carcter de escape delante de ellos. a Se ignoran los comentarios. Un comentario en una expresin regular detallada o es exactamente igual que en Python: comienza en el carcter # termina al a acabarse la l nea. En este caso, el comentario est incluido en una cadena de a texto de varias l neas en lugar de estar directamente en ti cdigo fuente, pero o por lo dems es exactamente lo mismo. a

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

>>> p a t t e r n = M{ 0 , 3 } (CM|CD|D?C{ 0 , 3 } ) (XC| XL | L?X{ 0 , 3 } ) ( IX | IV |V? I { 0 , 3 } ) $ >>> r e . s e a r c h ( p a t t e r n , < s r e . SRE Match o b j e c t >>> r e . s e a r c h ( p a t t e r n , < s r e . SRE Match o b j e c t >>> r e . s e a r c h ( p a t t e r n , < s r e . SRE Match o b j e c t >>> r e . s e a r c h ( p a t t e r n ,

# # # # # # # # #

comienzo de l a cadena u n i d a d e s de m i l l a r 0 a 3 M c e n t e n a s 900 (CM) , 400 (CD) , 0 300 ( 0 a 3 C) , o 500 800 (D, s e g u i d o po 0 a 3 C) d e c e n a s 90 (XC) , 40 (XL) , 0 30 ( 0 a 3 X) , o 50 80 (L , s e g u i d o de 0 a 3 X) u n i d a d e s 9 ( IX ) , 4 ( IV ) , 03 ( 0 a 3 I ) , o 58 (V, s e g u i d o de 0 a 3 I ) f i n de l a cadena

M , r e .VERBOSE) a t 0x008EEB48> MCMLXXXIX , r e .VERBOSE) a t 0x008EEB48> MMMDCCCLXXXVIII , r e .VERBOSE) a t 0x008EEB48> M )

1. Lnea 12: Lo ms importante es recordar que cuando utilizas expresiones re a gulares detalladas debes aadir un parmetro ms a la llamada a la funcin: n a a o re.VERBOSE es una constante denida en el mdulo re que permite indicar a la o funcin que el patrn debe interpretarse como una expresin regular detallao o o da. Como puedes ver, este patrn tiene muchos espacios (que son ignorados) y o varios comentarios (tambin ignorados). Una vez se han suprimido los espacios e y los comentarios, la expresin regular es exactamente la misma que viste en o la primera parte de este cap tulo, solo que mucho ms legible. a 2. Lnea 14: Esta expresin encuentra en primer lugar el comienzo de la cadena, o luego una de las tres posibles M, luego CM, luego L y las tres posibles X, luego IX y se termina con el n de la cadena.

5.6. CASO DE ESTUDIO: ANALISIS DE NUMEROS DE TELEFONO

129

3. Lnea 16: Esta expresin encuentra en primer lugar el comienzo de la cadena, o luego las tres posibles M, luego D y las tres posibles C, luego L y las tres posibles X, luego V y las tres posibles I y se termina con el n de la cadena. 4. Lnea 18: No se cumple. Porqu? Porque no se le ha pasado el parmetro e a re.VERBOSE, por lo que la funcin re.search() est tratando a la expresin o a o regular como si no fuese detallada, por lo que ha intentado encontrar en la cadena M todos los espacios y comentarios que hemos introducido en el patrn. o Python no puede detectar automticamente si una expresin es detallada o a o no. Python asume siempre que una expresin regular es compacta, a no ser o que se le diga lo contrario.

5.6.

Caso de estudio: anlisis de n meros de telfono a u e

Hasta el momento te has concentrado en identicar patrones completos. O el patrn coincide o no. Pero las expresiones regulares permiten hacer mucho ms. o a Cuando una cadena de texto coincide con el patrn de la expresin regular puedes o o recuperar trozos de la cadena a partir del objeto que se retorna. Puedes recuperar qu partes de la cadena han coincidido con qu partes de la expresin regular. e e o Este ejemplo est sacado de otro problema a real que me encontr en el trabajo. El problee zd coincide con cualquier d gima: analizar un nmero de telfono americano. u e to numrico (0-9). zD coincide e El cliente quer que el nmero se introdujese a u con cualquier carcter que no a de forma libre (en un unico campo de texto en sea d gito. pantalla), pero luego quer almacenar en la baa se de datos de la compa de forma separada na el cdigo de area, principal, nmero y opcionalmente, una extensin. Mir un poco o u o e en la web buscando ejemplos de expresiones regulares que hicieran esto, pero no encontr ninguna sucientemente permisiva. e A continuacin muestro algunos ejemplos de nmeros de telfono que necesio u e taba que se pudieran aceptar: 800-555-1212 800 555 1212 800.555.1212 (800) 555-1212 800-555-1212-1234

130 800-555-1212x1234 800-555-1212 ext. 1234 work 1-(800) 555.1212 #1234

CAP ITULO 5. EXPRESIONES REGULARES

Cunta variedad! En cada uno de los casos anteriores necesitaba identicar a el cdigo de rea 800, el troncal 500 y el resto del nmero 1212. Asimismo, para o a u aquellos que presentaban la extensin necesitaba conocer que era 1234. o
1 2 3 4 5 6 7 8 >>> phonePattern = r e . c o m p i l e ( r ( \ d{3}) (\d{3}) (\d { 4 } ) $ ) >>> phonePattern . s e a r c h ( 800 555 1212 ) . g r o u p s ( ) ( 800 , 555 , 1212 ) >>> phonePattern . s e a r c h ( 800 555 1212 1234 ) >>> phonePattern . s e a r c h ( 800 555 1212 1234 ) . g r o u p s ( ) Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> A t t r i b u t e E r r o r : NoneType o b j e c t has no a t t r i b u t e g r o u p s

1. Lnea 1: Las expresiones regulares siempre se leen de izquierda a derecha. Esta detecta el comienzo de la l nea y luego (zd{3}). Qu es (zd{3})? Bueno, zd e signica cualquier d gito numrico (0 a 9). El {3} signica que coincida e exactamente tres veces (tres d gitos en este caso), es una variacin de la o sintaxis {n,m} que viste antes. Al ponerlo todo entre parntesis se est diciendo e a identica exactamente tres d gitos numricos y recurdalos como un grupo e e para que te pueda preguntar por ellos ms tarde a 2. Lnea 2: Para acceder a los grupos, que se guardan durante la bsqueda de la u coincidencia de la cadena con expresin regular, se utiliza el mtodo groups() en o e el objeto que devuelve el mtodo search(). Este mtod (groups()) retornar una e e a tupla con tantos elementos como grupos se denieran en la expresion regular. En este caso se han denido tres grupos, dos con tres d gitos y un tercero con cuatro. 3. Lnea 4: Esta expresin regular no es perfecta, porque no es capaz de ma o nejar nmeros telefnicos con extensiones. Por eso ser necesario expandir la u o a expresin regular. o 4. L nea 5: Y en esta l nea se observa el porqu no debes encadenar en una unica e sentencia search() y group(). Si la funcin search() no encuentra ninguna coino cidencia en la cadena devuelve None, no un objeto de coincidencia. La llamada a None.groups() eleva una excepcin perfectamente clara: None no dispone de o la funcin groups(). (Por supuesto, no ser tan fcil ni tan obvio si la excepcin o a a o sucede en lo ms profundo de tu programa. S hablo por experiencia.) a ,

5.6. CASO DE ESTUDIO: ANALISIS DE NUMEROS DE TELEFONO


1 2 3 4 5 6 7 >>> phonePattern = r e . c o m p i l e ( r ( \ d{3}) (\d{3}) (\d{4}) (\d+)$ ) >>> phonePattern . s e a r c h ( 800 555 1212 1234 ) . g r o u p s ( ) ( 800 , 555 , 1212 , 1234 ) >>> phonePattern . s e a r c h ( 800 555 1212 1234 ) >>> >>> phonePattern . s e a r c h ( 800 555 1212 ) >>>

131

1. Lnea 1: Esta expresion regular es prcticamente igual a la anterior. Como a antes, coincide con el inicio de la cadena, luego con un grupo de tres d gitos decimales, luego con el guin, otro grupo de tres d o gitos decimales, otro guin, o y luego con el grupo de cuatro d gitos decimales. Lo que es nuevo es que despus debe coincidir con otro guin seguido de uno o ms d e o a gitos decimales, para terminar con el n de la cadena. 2. Lnea 2: El mtodo groups() ahora retorna una tupla de cuatro elementos, e puesto que la expresin regular ahora dene cuatro grupos a recordar. o 3. Lnea 4: Desafortunadamente esta expresin regular tampoco es la solucin o o denitiva, porque asume siempre que las diferentes partes del telfono estn e a separadas por guiones. Qu pasa si estn separadas por puntos, espacios o e a comas? Necesitas una solucin ms general para que coincida con diferentes o a tipos de separadores. 4. Lnea 6: Ups! No solamente no hace todo lo que queremos sino que en reali dad es un paso atrs, puesto que ahora no es capaz de identicar nmeros de a u telfono sin extensiones. Esto no era lo que quer si la extensin est ah quiee as, o a res conocerla, pero si no est sigues queriendo conocer las diferentes partes del a nmero de telfono. u e El siguiente ejemplo muestra una expresin regular para manejar diferentes o separadores entre las diferentes partes del nmero de telfono. u e
1 2 3 4 5 6 7 8 9 >>> phonePattern = r e . c o m p i l e ( r ( \ d { 3 } ) \D+(\d { 3 } ) \D+(\d { 4 } ) \D+(\d+)$ ) >>> phonePattern . s e a r c h ( 800 555 1212 1234 ) . g r o u p s ( ) ( 800 , 555 , 1212 , 1234 ) >>> phonePattern . s e a r c h ( 800 555 1212 1234 ) . g r o u p s ( ) ( 800 , 555 , 1212 , 1234 ) >>> phonePattern . s e a r c h ( 80055512121234 ) >>> >>> phonePattern . s e a r c h ( 800 555 1212 ) >>>

132

CAP ITULO 5. EXPRESIONES REGULARES

1. Lnea 1: Agrrate a tu sombrero. Primero se busca la coincidencia del inicio a de la cadena, luego un grupo de tres caracteres y luego zD+. Qu es eso? e Bueno, zD coincide con cualquier carcter que no sea d a gito, y + signica 1 o ms. Por eso esta expresin zD+ encuentra uno o ms caracteres que no a o a sean d gitos. Esto es lo que vamos a utilizar para intentar identicar muchos tipos diferentes de separadores (en lugar de unicamente el guin). o 2. Lnea 2: El uso de zD+ en lugar de - signica que puedes encontrar nmeros u de telfono que utilicen espacios como separadores en lugar de guiones. e 3. Lnea 4: Por supuesto an se encuentran los nmeros de telfonos que utilizan u u e como separador el guin. o 4. Lnea 6: Desafortunadamente an no tenemos la respuesta nal porque se u est asumiendo que debe haber al menos un separador. Qu pasa si el nmero a e u no tiene ningn separador? u 5. L nea 8: Ups! Y an no se ha arreglado el problema de que sea opcional, y no u obligatoria, la existencia de las extensiones. Ahora tienes dos problemas, pero se pueden resolver ambos con la misma tcnica. e El siguiente ejemplo muestra una expresin regular para manejar nmeros o u telefnicos sin separadores. o
1 2 3 4 5 6 7 8 9 >>> phonePattern = r e . c o m p i l e ( r ( \ d { 3 } ) \D ( \ d { 3 } ) \D ( \ d { 4 } ) \D ( \ d ) $ ) >>> phonePattern . s e a r c h ( 80055512121234 ) . g r o u p s ( ) ( 800 , 555 , 1212 , 1234 ) >>> phonePattern . s e a r c h ( 8 0 0 . 5 5 5 . 1 2 1 2 x1234 ) . g r o u p s ( ) ( 800 , 555 , 1212 , 1234 ) >>> phonePattern . s e a r c h ( 800 555 1212 ) . g r o u p s ( ) ( 800 , 555 , 1212 , ) >>> phonePattern . s e a r c h ( ( 8 0 0 ) 5 5 5 1 2 1 2 x1234 ) >>>

1. Lnea 1: El unico cambio que has hecho desde el ultimo caso es cambiar + por *. En lugar de zD+ entre las partes del nmero de telfono, ahora utilizas u e zD*. Recuerdas que + signica 1 o ms? Pues * signica 0 o ms. Por eso a a ahora deber ser capaz de encontrar los nmeros de telfono incluso cuando as u e no exista ningn separador. u 2. Lnea 2: Funciona! Porqu? Encuentras el comienzo de la cadena, luego un e grupo de tres d gitos (800), luego cero caracteres no nmericos, luego otro u grupo de tres d gitos (555), luego cero caracteres no numricos, luego un grupo e de cuatro d gitos (1212), luego cero caracteres no numricos, luego un grupo e arbitrario de d gitos (1234) y luego el n de la cadena.

5.6. CASO DE ESTUDIO: ANALISIS DE NUMEROS DE TELEFONO

133

3. Lnea 4: Tambin funcionan otras variaciones: puntos en lugar de guiones, y e espacios o x antes de la extensin. o 4. Lnea 6: Tambin se ha resuelto nalmente el problema de las extensiones. e Vuelven a ser opcionales. Si no se encuentra la extensin, la funcin groups() o o sigue retornando una tupla de cuatro elementos, pero el cuarto elemento es una cadena vac a. 5. Lnea 8: Odio tener que ser el portador de malas noticias, pero an no hemos u terminado. Cul es el problema aqu Existe un carcter extra antes del cdigo a ? a o de rea pero la expresin regular asume que el primer carcter debe ser lo a o a primero de la cadena. No pasa nada, puedes utilizar la misma tcnica de cero e o ms caracteres no numricos para saltarte todos los caracteres no numricos a e e iniciales. El siguiente ejemplo muestra como manejar los caracteres previos al nmero u de telfono: e
1 2 3 4 5 6 7 >>> phonePattern = r e . c o m p i l e ( r \D ( \ d { 3 } ) \D ( \ d { 3 } ) \D ( \ d { 4 } ) \D ( \ d ) $ ) >>> phonePattern . s e a r c h ( ( 8 0 0 ) 5 5 5 1 2 1 2 e x t . 1234 ) . g r o u p s ( ) ( 800 , 555 , 1212 , 1234 ) >>> phonePattern . s e a r c h ( 800 555 1212 ) . g r o u p s ( ) ( 800 , 555 , 1212 , ) >>> phonePattern . s e a r c h ( work 1 (800) 5 5 5 . 1 2 1 2 #1234 ) >>>

1. Lnea 1: Esto es igual que en el ejemplo anterior salvo que ahora ests buscando a zD*, cero o ms caracteres numricos, antes del primero grupo (el cdigo de a e o area). Observa que no se recuerdan esos caracteres, no estn en un grupo a (no estn entre parntesis). Si aparecen, simplemente se saltan y luego se a e comienza a reconocer el codigo de area que ser almacenado por la bsqueda a u (al encontrarse entre parntesis). e 2. Lnea 2: Ahora puedes reconocer satisfactoriamente el nmero de telfono u e incluso con el parntesis de la izquierda antes del cdigo de area. El parntesis e o e de la derecha del cdigo de area ya estaba controlado, se trata como un carcter o a no numrico y se detecta con el zD* posterior al primer grupo. e 3. Lnea 4: Solamente, para tener la seguridad de que no has roto nada que fun cionase. Puesto que los caracteres iniciales son totalmente opcionales, primero detecta el comienzo de la cadena, luego cero caracteres no numricos, luego un e grupo de tres d gitos de rea (800), luego un carcter no numrico (el guin), a a e o luego un grupo de tres d gitos (555), luego un carcter no numrico (el guin), a e o

134

CAP ITULO 5. EXPRESIONES REGULARES luego un grupo de cuatro d gitos (1212), luego cero caracteres no numricos, e luego un grupo de cero d gitos opcionales, para terminar con el nal de la cadena.

4. Lnea 6: Por eso las expresiones regulares me hacen querer sacarme los ojos con un objeto punzante. Porqu no funciona con este nmero de telfono? Porque e u e hay un 1 antes del cdigo de rea pero has asumido que todos los caracteres o a que van delante del cdigo de area sern no numricos (zD*), Aarggghhhh!!! o a e Vamos a resumir por un momento. Hasta ahora todas las expresiones regulares han buscado desde el comienzo de la cadena. Pero ahora ves que existe un nmero u indeterminado de cosas al comienzo que puede interesarte ignorar. En lugar de intentar hacer coincidir todas las combinaciones posibles lo mejor es que las ignores. Vamos a intentarlo de una forma distinta: sin expresar expl citamente el comienzo de la cadena. Esta opcin se muestra en el siguiente ejemplo. o
1 2 3 4 5 6 7 >>> phonePattern = r e . c o m p i l e ( r ( \ d { 3 } ) \D ( \ d { 3 } ) \D ( \ d { 4 } ) \D ( \ d ) $ ) >>> phonePattern . s e a r c h ( work 1 (800) 5 5 5 . 1 2 1 2 #1234 ) . g r o u p s ( ) ( 800 , 555 , 1212 , 1234 ) >>> phonePattern . s e a r c h ( 800 555 1212 ) ( 800 , 555 , 1212 , ) >>> phonePattern . s e a r c h ( 80055512121234 ) ( 800 , 555 , 1212 , 1234 )

1. Lnea 1: Observa la ausencia del en esta expresin regular, ya no vas a obligar o a que la coincidencia sea desde el comienzo de la cadena. No hay nada que diga que tienes que hacer coincidir la cadena completa. El motor de proceso de las expresiones regulares se encargar de hacer el trabajo duro descubriend a el lugar en el que la cadena de entrada comienza a coincidir. 2. Lnea 2: Ahora puedes analizar un nmero de telfono que incluya caracteres u e y un d gito previo, adems de cualquier clase de separadores alrededor de cada a parte del nmero. u 3. L nea 4: Test de seguridad. An funciona. u 4. Lnea 6: Y esto tambin. e Ves qu rpido comienza uno a perder el control de las expresiones regulares? e a Echale un vistazo a cualquiera de los ejemplos anteriores. Puedes identicar an u las diferencias entre uno y otro? Aunque puedas comprender la respuesta nal (y es la ultima respuesta, y si encuentras un caso para el que no funcione no quiero conocerlo!), vamos a escribir

5.7. RESUMEN

135

la expresin regular de forma detallada antes de que se te olvide porqu elegiste las o e opciones que elegiste:
1 >>> phonePattern = r e . c o m p i l e ( r 2 # No busca e l i n i c i o , puede empezar en c u a l q u i e r s i t i o 3 ( zd { 3 } ) # e l c d i g o de r e a t i e n e t r e s d g i t o s ( e j . 800 ) o a 4 zD # separador opcional 5 ( zd { 3 } ) # e l t r o n c a l s i n 3 d g i t o s ( e j . 555 ) 6 zD # separador opcional 7 ( zd { 4 } ) # e l r e s t o d e l n mero : 4 d g i t o s ( e j . 1212 ) u 8 zD # separador opcional 9 ( zd ) # e x t e n s i n o p c i o n a l , c u a l q u i e r n mero de d g i t o s o u 10 $ # f i n de l a cadena 11 , r e .VERBOSE) 12 >>> phonePattern . s e a r c h ( work 1 (800) 5 5 5 . 1 2 1 2 #1234 ) . g r o u p s ( ) 13 ( 800 , 555 , 1212 , 1234 ) 14 >>> phonePattern . s e a r c h ( 800 555 1212 ) 15 ( 800 , 555 , 1212 , )

1. Lnea 12: Aparte de estar escrita en varias l neas, esta expresin es exactao mente la misma que la del ejemplo anterior, por lo que analiza las mismas cadenas. 2. Lnea 14: Validacin nal de seguridad. S an funciona. Has nalizado. o , u

5.7.

Resumen

Solamente has visto la punta ms pequea del iceberg de lo que las expresiones a n regulares pueden hacer. En otras palabras, aunque ests totalmente abrumado por e ellas ahora mismo, creme, no has visto casi nada de ellas an. e u Deber estar familiarizado con las siguientes tcnicas: as e

z coincide con el comienzo de la cadena. z$ coincide con el nal de la cadena. zb coincide con un l de palabra. mite zd coincide con cualquier d numrico. gito e zD coincide con cualquier carcter no numrico. a e
x? coincide con un carcter x 0 a 1 veces. a

136

CAP ITULO 5. EXPRESIONES REGULARES x* coincide con un carcter x 0 o ms veces. a a x+ coincide con un carcter x 1 o ms veces. a a x{n,m} coincide con carcter x entre n y m veces. a (abc) coincide con a o b o c. (x) en general es un grupo a recordar. Puedes obtener el valor de lo que ha coincidido mediante el mtodo group() del objeto retornado por la llamada a e re.search().

Las expresiones regulares son muy potentes pero no son la solucin a todos los o problemas. Deber aprender a manejarlas de forma que sepas cundo son apropiaas a das, cundo pueden ayudarte a resolver un problema y cundo te producirn ms a a a a problemas que los que resuelven.

Cap tulo 6 Cierres y generadores


Nivel de dicultad:   Mi forma de deletrear es temblorosa. Deletreo bien pero me tiembla la voz as que las letras acaban en los lugares equivocados. Winnie the Pooh

6.1.

Inmersin o

Por razones que superan toda comprensin, siempre he estado fascinado por o los lenguajes. No por los lenguajes de programacin. Bueno s lenguajes de proo , gramacin, pero tambin idiomas naturales. Toma como ejemplo el ingls, es un o e e idioma esquizofrnico que toma prestadas palabras del Alemn, Francs, Espaol e a e n y lat (por decir algunos). En realidad tomar prestado es una frase equivocada, n saquea ser ms adecuada. O quizs asimila como los Borg, s eso me gusta: a a a Somos los Borg. Aadiremos tus particularidades lingu n sticas y etimolgio cas a las nuestras. Es intil que te resistas. u En este cap tulo vas a aprender sobre nombres en plural. Tambin sobre fune ciones que retornan otras funciones, expresiones regulares avanzadas y generadores. Pero primero hablemos sobre cmo crear nombres en plural. (Si no has le o do el cap tulo 5 sobre expresiones regulares ahora puede ser un buen momento; este cap tulo asume una comprensin bsica de ellas y rpidamente desciende a usos ms o a a a avanzados). 137

138

CAP ITULO 6. CIERRES Y GENERADORES

Si has crecido en un pa de habla inglesa o has aprendido ingls en el mbito s e a escolar, probablemente ests familiarizado con sus reglas bsicas de formacin del e a o plural: Si una palabra termina en S, X o Z adele ES. Bass se convierte en basses, na fax en faxes y waltz en waltzes. Si una palabra termina en una H sonora, aade ES, si termina en una H muda, n aade S. Qu es una H sonora? una que se combina con otras letras para n e crear un sonido que se puede escuchar. Por eso coach se convierte en coaches y rash en rashes, porque se pronuncian tanto la CH como la SH. Pero cheetah se convierte en cheetahs, puesto que la H es muda. Si una palabra termina en una Y que suene como I, se cambia la Y por IES; si la Y se combina con una vocal para de otro modo, simplemente aade una S. n Por eso vacancy se convierte en vacancies y day se convierte en days. Si todo lo dems falla, simplemente aadie uan S y cruza los dedos por que a n hayas acertado. (S que hay muchas excepciones, man se convierte en men, woman en women, e sin embargo human se convierte en humans. Mouse se convierte en mice y louse en lice, pero house se convierte en houses. Knife en knives y wife en wives, pero lowlife se convierte en lowlifes. Y todo ello sin meterme con las palabras que tienen su propio plural, como sheep, deer y haiku). Otros idiomas, por supuesto, son completamente diferentes. Vamos a disear una librer de Python que pase a plural los nombres ingleses n a de forma automtica. Vamos a comenzar con estas cuatro reglas, pero recuerda que, a inevitablemente, necesitars aadir ms. a n a

6.2.

Lo s, vamos a usar expresiones regulares! e

As que ests analizando palabras, lo que, al menos en ingls, signica que a e ests analizando cadenas de caracteres. Tienes reglas que requieren la existencida de a diferentes combinaciones de caracteres, y despus de encontrarlas necesitas hacerles e modicaciones. Esto parece un buen trabajo para las expresiones regulares!

6.2. LO SE, VAMOS A USAR EXPRESIONES REGULARES!


1 2 3 4 5 6 7 8 9 10 11 import r e def p l u r a l ( nombre ) : i f r e . s e a r c h ( [ s x z ] $ , nombre ) : return r e . sub ( $ , e s , nombre ) e l i f r e . s e a r c h ( [ a e i o u d g k p r t ] h$ , nombre ) : return r e . sub ( $ , e s , nombre ) e l i f r e . s e a r c h ( [ a e i o u ] y$ , nombre ) : return r e . sub ( y$ , i e s , nombre ) else : return nombre + s

139

1. Lnea 4: Esto es una expresin regular que utiliza una sintaxis que no has o visto en el cap tulo dedicado a ellas. Los corchetes cuadrados indican que se encuentre exactamente uno de los caracteres encerrados entre ellos. Por eso [xyz] signica o s o x o z, pero unicamente uno de ellos. El s mbolo $ s deber serte familiar, coincide con el nal de la cadena. Al combinarlos esta a expresin regular comprueba si la variable nombre naliza con s, x o z. o 2. Lnea 5: La funcin re.sub() ejecuta una sustitucin de cadena de texto basndo o o a se en una expresin regular. o Vamos a ver en detalle las sustituciones de texto utilizando expresiones regulares.
1 2 3 4 5 6 7 8 9 >>> import r e >>> r e . s e a r c h ( [ abc ] , Mark ) < s r e . SRE Match o b j e c t a t 0x001C1FA8> >>> r e . sub ( [ abc ] , o , Mark ) Mork >>> r e . sub ( [ abc ] , o , r o c k ) rook >>> r e . sub ( [ abc ] , o , c a p s ) oops

1. Lnea 2: La cadena Mark contiene a, b o c? s contiene la a. , 2. Lnea 4: Ok, ahora vamos a buscar a, b o c y reemplazarlos por una o. Mark se convierte en Mork. 3. Lnea 6: La misma funcin convierte rock en rook. o 4. Lnea 8: Podr creer que caps se convierte en oaps, pero no lo hace. re.sub() as reemplaza todas las coincidencias, no solamente la primera. Por eso, esta expresin regular convierte caps en oops, porque coinciden tanto la c como la a, o as que ambas se convierten en o.

140

CAP ITULO 6. CIERRES Y GENERADORES Y ahora volvamos a la funcin plural()... o

1 2 3 4 5 6 7 8 9

def p l u r a l ( nombre ) : i f r e . s e a r c h ( [ s x z ] $ , nombre ) : return r e . sub ( $ , e s , nombre ) e l i f r e . s e a r c h ( [ a e i o u d g k p r t ] h$ , nombre ) : return r e . sub ( $ , e s , nombre ) e l i f r e . s e a r c h ( [ a e i o u ] y$ , nombre ) : return r e . sub ( y$ , i e s , nombre ) else : return nombre + s

1. Lnea 3: Aqu ests reemplazando el nal de la cadena (que se encuentra con $) a con la cadena es. En otras palabras, ests aadiendo es al nal de la cadena. a n Podr conseguir lo mismo con la concatenacin de cadenas, por ejemplo as o nombre + es, pero he elegido utilizar expresiones regulares para cada regla, por razones que quedarn claras ms adelante. a a 2. Lnea 4: Mira atentamente, esta es otra variacin nueva. El en el primer o carcter de los corchetes indica algo especial: negacin. [bc] signica busa o a ca por un unico carcter pero que sea cualquiera salvo a, b o c. Por eso a [eioudgkprt] signica que se busque por cualquier carcter salvo los indicados. a a Luego ese carcter deber tener detrs una h y despus de ella debe venir el a a a e nal de la cadena. Estamos buscando por palabras que terminen en H sonoras. 3. Lnea 6: Aqu seguimos el mismo patrn, busca palabras que terminen en Y, o en las que delante de ella no exista una vocal. Estamos buscando por palabras que terminen en Y que suenen como I. Veamos en detalle el uso de la negacin en expresiones regulares. o
1 2 3 4 5 6 7 8 9 >>> import r e >>> r e . s e a r c h ( [ a e i o u ] y$ , vacancy ) < s r e . SRE Match o b j e c t a t 0x001C1FA8> >>> r e . s e a r c h ( [ a e i o u ] y$ , boy ) >>> >>> r e . s e a r c h ( [ a e i o u ] y$ , day ) >>> >>> r e . s e a r c h ( [ a e i o u ] y$ , p i t a ) >>>

1. Lnea 2: La palabra vacancy coincide con esta expresin regular, porque naliza o en cy, y la c no es una vocal.

6.2. LO SE, VAMOS A USAR EXPRESIONES REGULARES!

141

2. Lnea 4: La palabra boy no coincide porque naliza en oy, y la expresin regular o dice expresamente que delante de la y no puede haber una o. La palabra day tampoco coincide por una causa similar, puesto que termina en ay. 3. Lnea 8: La palabra pita no coincide, puesto que no termina en y.
1 >>> r e . sub ( y$ , i e s , vacancy ) 2 vacancies 3 >>> r e . sub ( y$ , i e s , agency ) 4 agencies 5 >>> r e . sub ( ( [ a e i o u ] ) y$ , r \1 i e s , vacancy ) 6 vacancies

1. Lnea 1: Esta expresin regular convierte vacancy en vacancies y agency en o agencies, que es lo que quer Observa que tambin convierte boy en boies, as. e pero eso no pasar porque antes habremos efectuado un re.search() para desa cubrir si debemos hacer la sustitucin re.sub(). o 2. Lnea 5: Aunque sea de pasada, me gustar apuntar que es posible combinar a las dos expresiones regulares en una unica sentencia (la primera expresin o regular para descubrir si se debe aplicar una regla y la segunda para aplicarla manteniendo el texto correcto). Se muestra como quedar La mayor parte te a. debe ser familiar del cap tulo dedicado a las expresiones regulares. Utilizamos un grupo para recordar el carcter que se encuentra delante de la letra y. a Luego, en la cadena de sustitucin se utiliza una sintaxis nueva z1, que sirve o para indicar que en ese punto se debe poner el valor del grupo guardado. En este ejemplo, el valor del grupo es la letra c de delante de la letra y; cuando se efecta la sustitucin, se sustituye la c en su mismo lugar, y los caracteres u o ies en el lugar de la y. (Si se hubiesen guardado ms grupos, se podr incluir a an con z2, z3 y as sucesivamente. Las sustitucin mediante el uso de expresiones regulares es un mecanismo muy o potente, y la sintaxis 1 lo hace an ms. Pero combinar toda la operacin en una unica expresin regular u a o o la hace mucho ms dif de leer al no describir directamente la forma en la que se a cil explicaron las reglas del plural. Estas reglas dec originalmente algo as como si an la palabra termina en S, X o Z, entonces aade ES. Si se echa un vistazo al cdigo n o de la funcin, las dos l o neas de cdigo dicen casi exactemente eso mismo. o

142

CAP ITULO 6. CIERRES Y GENERADORES

6.3.

Una lista de funciones

Ahora vas a aadir un nuevo nivel de abstracin. Comenzaste por denir una n o lista de reglas: si pasa esto, haz esto oto, en caso contrario ve a la siguiente regla. Vamos a complicar un poco parte del programa para poder simplicar otra parte.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import r e def match sxz ( noun ) : return r e . s e a r c h ( [ s x z ] $ , noun ) def a p p l y s x z ( noun ) : return r e . sub ( $ , e s , noun ) def match h ( noun ) : return r e . s e a r c h ( [ a e i o u d g k p r t ] h$ , noun ) def a p p l y h ( noun ) : return r e . sub ( $ , e s , noun ) def match y ( noun ) : return r e . s e a r c h ( [ a e i o u ] y$ , noun ) def a p p l y y ( noun ) : return r e . sub ( y$ , i e s , noun ) def m a t c h d e f a u l t ( noun ) : return True def a p p l y d e f a u l t ( noun ) : return noun + s r u l e s = ( ( match sxz , a p p l y s x z ) , ( match h , a p p l y h ) , ( match y , a p p l y y ) , ( match default , apply default ) ) def p l u r a l ( noun ) : f o r m a t c h e s r u l e , a p p l y r u l e in r u l e s : i f m a t c h e s r u l e ( noun ) : return a p p l y r u l e ( noun )

1. Funcin match y: Ahora cada regla de bsqueda se convierte en una funcin o u o que devuelve el resultado de la bsqueda: re.search(). u

6.3. UNA LISTA DE FUNCIONES

143

2. Funcin apply y: Cada regla de sustitucin tine su propia funcin que llama o o o a re.sub() para aplicar la regla apropiada. 3. Lnea 27: En lugar de tener una funcin plural() con mltiples reglas, tenemos o u la estructura rules, que es una secuencia formada por parejas en la que cada una de ellas est formada, a su vez, por dos funciones. a 4. Lnea 34: Puesto que las reglas se han pasado las funciones contenidas en la estructura de datos, la nueva funcin plural() puede reducirse a unas cuantas o l neas de cdigo. Mediante el uso de un bucle for puedes obtener en cada o paso las funciones de bsqueda y sustitucin de la estructura de datos rules. u o a En la primera iteracin del bucle for la variable matches rule referenciar a la o funcin match sxz y la variable apply rule referenciar a la funcin apply sxz. o a o En la segunda iteracin (asumiendo que alcanzas a ello), se le asigna match h o a matches rule y apply h a apply rule. Est garantizado que la funcin retorna a o algo en todos los caso, porque la ultima regla de bsqueda (match default) u retorna True siempre, lo que signica que se aplicar en ultima instancia la a regla correspondiente (match default). La razn por la que esta tcnica funciona o e es que todo en Python es un objeto, funciones La variable rules es una seincluidas. La estructura de datos rules contiene cuencia de pares de funciones. funciones nombres de funcin, sino objetos funo cin reales. Cuando se asignan en el bucle for o las variables matches rule y apply rule apuntan a funciones reales que puedes llamar. En la primera iteracin del bucle for esto supone que se llama la funcin mato o ches sxz(noun) y si retorna una coincidencia se llama a la funcin apply sxz(noun). o Si este nivel adicional de abstraccin te resulta confuso, intenta verlo as Este o . bucle for es equivalente a lo siguiente:
1 2 3 4 5 6 7 8 9 def p l u r a l ( noun ) : i f match sxz ( noun ) : return a p p l y s x z ( noun ) i f match h ( noun ) : return a p p l y h ( noun ) i f match y ( noun ) : return a p p l y y ( noun ) i f m a t c h d e f a u l t ( noun ) : return a p p l y d e f a u l t ( noun )

La ventaja es que ahora la funcin plural() es ms simple. Toma una secuencia o a de reglas, denidas en otra parte y cuyo nmero puede variar, e itera a travs de u e ella de forma genrica. e

144

CAP ITULO 6. CIERRES Y GENERADORES

1. Obtiene una pareja: funcin de bsqueda - funcin de sustitucin. o u o o 2. Comprueba si la funcin de bsqueda retorna True. o u 3. Coincide? Entonces ejecuta la funcin de sustitucin y devuelve el resultado. o o 4. No coincide? Vuelve al paso uno. La reglas pueden denirse en otra parte, de cualquier forma. A la funcin o plural() no le importa. Merec la pena aadir este nivel de abstraccin? Tal vez an no te lo parezca. a n o u Vamos a considerar lo que supondr aadir una nueva regla. En el primer ejemplo, a n requerir aadir una sentencia if a la funcin plural(). En este segundo ejemplo, a n o n requeriria aadir dos funciones match algo() y apply algo() y luego aadirlas a la n secuencia rules para especicar en qu orden deben llamarse estas nuevas funciones e en relacin a las reglas que ya exist o an. Pero realmente esto que hemos hecho es un hito en el camino hacia la siguiente seccin. Sigamos... o

6.4.

Una lista de patrones

Realmente no es necesario denir una funcin para cada patrn de bsqueda o o u y de sustitucin. Nunca los llamas directamente; los aades a la secuencia de reglas o n (rules) y los llamas desde ah Es ms, cada funcin sigue uno de los dos patrones. . a o Todas las funciones de bqueda llaman a re.search() y todas las funciones de sustiu tucin llaman a re.sub(). Vamos a sacar los patrones para que denir nuevas reglas o sea ms sencillo. a
1 2 3 4 5 6 7 8 import r e def b u i l d m a t c h a n d a p p l y f u n c t i o n s ( p a t t e r n , s e a r c h , r e p l a c e ) : def m a t c h e s r u l e ( word ) : return r e . s e a r c h ( p a t t e r n , word ) def a p p l y r u l e ( word ) : return r e . sub ( s e a r c h , r e p l a c e , word ) return ( m a t c h e s r u l e , a p p l y r u l e )

1. Lnea 3: build match and apply functions es una funcin que construye otras o funciones dinmicamente. Toma los parmetros y dene la funcin matches rule() a a o que llama a re.search() con el pattern que se haya pasado y el parmetro word a que se pasar a la funcin cuando se llame en el futuro. Vaya! a o

6.4. UNA LISTA DE PATRONES

145

2. Lnea 6: La construccin de la funcin de sustitucin es similar. Es una funcin o o o o que toma un parmetro word y llama a re.sub() con l y los parmetros search a e a y replace que se pasaron a la funcin constructora. Esta tcnica de utilizar o e los valores de los parmetros exteriores a una funcin dentr ode una funcin a o o dinmica se denomina closures 1 . En el fondo se estn deniendo constantes a a que se utilizan dentro detro de la funcin que se est construyendo: la funcin o a o construida toma un unico parmetro (word) y los otros dos valores utilizados a (search y replace) son los que tuvieran almacenados en el momento en que se deni la funcin. o o 3. Lnea 8: Finalmente, la funcin retorna una tupla con las dos funciones recin o e creadas. Las constantes denidas dentro de esas funciones (pattern en la funcin o o match rule(), y search y replace en la funcin apply rule()) conservan los valores dentro de cada una de ellas, incluso despus de nalizar la ejecucin de la e o funcin constructora. Esto resulta ser muy prctico. o a Si te resulta muy confuso (y deber puesto que es algo bastante avanzado y a, extrao), puede quedarte ms claro cuando veas cmo usarlo. n a o
1 2 3 4 5 6 7 8 9 patterns = \ ( ( [ sxz ] $ , $ , ( [ a e i o u d g k p r t ] h$ , $ , ( ( qu | [ a e i o u ] ) y$ , y$ , ( $ , $ , ) rules = [ build match and apply for ( pattern , search ,

es ) , es ) , ies ) , s ) functions ( pattern , search , r e p l a c e ) r e p l a c e ) in p a t t e r n s ]

1. Lnea 1: Ahora las reglas se denen como una tupla de tuplas de cadenas (no son funciones). La primera cadena en cada tr es el patrn de bsqueda que o o u se usar en re.search() para descubrir si una cadena coincide. La segunda y la a tercera cadenas de cada grupo es la expresin de bsqueda y reemplazo que se o u utilizarn en re.sub() para modicar un nombre y ponerlo en plural. a 2. Lnea 6: Hay un ligero cambio aqu en la regla por defecto. En el ejemplo , anterior, la funcin match default() retornaba True, dando a entender que si o ninguna otra regla coincid el cdigo simplemente deber aadir una s al a, o a n nal de la palabra. Este ejemplo hacer algo que es funcionalmente equivalente. La expresin regular nal simplemente pregunta si la palabra tiene nal ($ o coincide con el nal de la cadena). Desde luego todas las cadenas tienen nal,
1

Nota del Traductor: en espaol se utiliza la palabra cierre para referirse a este trmino. n e

146

CAP ITULO 6. CIERRES Y GENERADORES incluso la cadena vac por lo que esta expresin siempre coincide. As que sirve a, o para el mismo propsito que la funcin match default() del ejemplo anterior: o o asegura que si no coincide una regla ms espec a ca, el cdigo aade una s al o n nal de la palabra.

3. Lnea 8: Esta l nea es magia. Toma la secuencia de cadenas de patterns y la convierte en una secuencia de funciones. Cmo? mapeando las cadenas o con la funcin build match and apply functions. Toma un triplete de cadenas y o llama a la funcin con las tres cadenas como argumentos. La funcin retorna o o una tupla de dos funciones. Esto signica que las variable rules acaba siendo equivalente a la del ejemplo anterior: una lista de tuplas, en la que cada una de ellas contiene un par de funciones. La primera funcin es la funcin de o o bsqueda que llama a re.search() y la segunda que es la funcin de sustitucin u o o que llama a re.sub(). Para nalizar esta versin del programa se muestra el punto de entrada al o mismo, la funcin plural(). o
1 2 3 4 def p l u r a l ( noun ) : f o r m a t c h e s r u l e , a p p l y r u l e in r u l e s : i f m a t c h e s r u l e ( noun ) : return a p p l y r u l e ( noun )

Como la lista rules es igual que en el ejemplo anterior (realmente lo es), no deber sorprenderte al ver que la funcin plural() no ha cambiado en nada. Es as o totalmente genrica; toma una lista de funciones de reglas y las llama en orden. e No le importa cmo se han denido las reglas. En el ejemplo anterior, se denieron o funciones separadas. Ahora se han creado funciones dinmicas al mapearlas con la a funcin build match and apply functions a partir de una serie de cadenas de texto. o No importa, la funcin plural() sigue funcionando igual. o

6.5.

Un chero de patrones

Hemos eliminado todo el cdigo duplicado y aadido sucientes abstracciones o n para que las reglas de formacin de plurales del ingls queden denidas en una lista o e de cadenas. El siguiente paso lgico es extraer estas reglas y ponerlas en un chero o separado, en el que se puedan modicar de forma separada del cdigo que las utiliza. o Primero vamos a crear el chero de texto que contenga las reglas que necesitas. No vamos a crear estructuras de datos complejas, simplemente cadenas de texto separadas por espacios en blanco en tres columnas. Vamos a llamarlo plural4-rules.txt.

6.5. UN FICHERO DE PATRONES


1 2 3 4 [ sxz ] $ [ a e i o u d g k p r t ] h$ [ a e i o u ] y$ $ $ $ y$ $ es es ies s

147

Ahora veamos cmo utilizar este chero de reglas. o


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import r e def b u i l d m a t c h a n d a p p l y f u n c t i o n s ( p a t t e r n , s e a r c h , r e p l a c e ) : def m a t c h e s r u l e ( word ) : return r e . s e a r c h ( p a t t e r n , word ) def a p p l y r u l e ( word ) : return r e . sub ( s e a r c h , r e p l a c e , word ) return ( m a t c h e s r u l e , a p p l y r u l e ) rules = [ ] with open ( p l u r a l 4 r u l e s . t x t , e n c o d i n g= u t f 8 ) a s p a t t e r n f i l e : f o r l i n e in p a t t e r n f i l e : p a t t e r n , s e a r c h , r e p l a c e = l i n e . s p l i t ( None , 3 ) r u l e s . append ( b u i l d m a t c h a n d a p p l y f u n c t i o n s ( pattern , search , r e p l a c e ))

1. Lnea 3: La funcin build match and apply functions no ha cambiado. An uti o u lizamos los cierres para construir dos funciones dinmicas por cada llamada, a que utilizan las variables denidas en la funcin externa. o 2. Lnea 11: La funcin global open() abre un chero y devuelve un objeto chero. o En este caso, el chero que estamos abriendo contiene las cadenas de texto que son los patrones de formacin de los nombres en plural La sentencia with crea o un contexto: cuando el bloque with termina, Python cerrar automticamente a a el chero, incluso si sucede una excepcin dentro del bloque with. Lo vers con o a ms detalle en el cap a tulo 11 dedicado a los cheros. 3. Lnea 12: La sentencia for lee los datos de un chero abierto: una l nea cada vez, y asigna el valor de dicha l nea a la variable line. Lo veremos en mayor detalle en el cap tulo 11 dedicado a los cheros. 4. Lnea 13: Cada l nea del chero contiene tres valores que estn separados a por espacios en blanco o tabuladores. Para obtenerlos, se utiliza el mtodo e de cadenas de texto split(). El primer parmetro del mtodo split() es None, a e que signica que trocea la cadena en cualquier espacio en blanco (tabuladores incluidos, sin distincin). El segundo parmetro es 3, que signca que trocea o a la cadena hasta 3 veces, y luego deje el resto de la l nea. Una l nea como [sxy]$ $ es se trocea en la siguiente lista [[sxy]$, $, es], lo que signica que

148

CAP ITULO 6. CIERRES Y GENERADORES pattern contendr el valor [sxy]$, search el valor $ y replace el valor es. a Como ves, esto hace mucho para tan poco cdigo escrito. o

5. Lnea 14: Finlmente, pasas los valores pattern, search y replace a la funcin o build match and apply functions, que retorna una tupla con las dos funciones creadas. Esta tupla se aade a la lista rules, por lo que, al nalizar, la lista n rules contiene la lista de funciones de bsqueda y sustitucin que la funcin u o o plural() necesita.

6.6.

Generadores

No ser estupendo tener una funcin genrica que fuese capaz de recuperar a o e el chero de reglas? Obtener las reglas, validar si hay coincidencia, aplicar la transformacin apropiada y seguir con la siguiente regla. Esto es lo unico que la funcin o o plural() tiene que hacer.
1 2 3 4 5 6 7 8 9 10 11 def r u l e s ( r u l e s f i l e n a m e ) : with open ( r u l e s f i l e n a m e , e n c o d i n g= u t f 8 ) a s p a t t e r n f i l e : f o r l i n e in p a t t e r n f i l e : p a t t e r n , s e a r c h , r e p l a c e = l i n e . s p l i t ( None , 3 ) y i e l d build match and apply functions ( pattern , search , r e p l a c e ) def p l u r a l ( noun , r u l e s f i l e n a m e= p l u r a l 5 r u l e s . t x t ) : f o r m a t c h e s r u l e , a p p l y r u l e in r u l e s ( r u l e s f i l e n a m e ) : i f m a t c h e s r u l e ( noun ) : return a p p l y r u l e ( noun ) r a i s e V a l u e E r r o r ( no matching r u l e f o r {0} . format ( noun ) )

Cmo funciona este cdigo? Vamos a verlo primero con un ejemplo interactivo. o o

6.6. GENERADORES
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 >>> def make counter ( x ) : ... print ( e n t r a n d o en make counter ) ... while True : ... yield x ... print ( incrementando x ) ... x = x + 1 ... >>> c o u n t e r = make counter ( 2 ) >>> c o u n t e r <g e n e r a t o r o b j e c t a t 0x001C9C10> >>> next ( c o u n t e r ) e n t r a n d o en make counter 2 >>> next ( c o u n t e r ) incrementando x 3 >>> next ( c o u n t e r ) incrementando x 4

149

1. Lnea 4: La presencia de la sentencia yield en la funcin make counter signica o que sta no es una funcin normal. Al llamarla lo que sucede es que se e o retorna un generador que puede utilizarse para generar sucesivos valores de x. 2. Lnea 8: Para crear una instancia del generador make counter simplemente hay que llamarlo como a cualquier otra funcin. Observa que esto en realidad no o ejecuta el cdigo de la funcin. Lo puedes comprobar porque la primera l o o nea o de la funcin make counter() llama a la funcin print() y en esta llamada no se o ha imprimido nada en pantalla. 3. Lnea 9: La funcin make counter() retorna un objeto generador. o 4. Lnea 11: El mtodo next() del generador retorna su siguiente valor. La primera e vez que ejecutas next() se ejecuta el cdigo de la funcin make counter() hasta o o la primera sentencia yield que se ejecute, y se retorna el valor que aparece en la sentencia yield2 . En este caso, el valor ser 2, porque originalmente se cre el a o generador con la llamdada make counter(2). 5. Lnea 14: Al llamar repetidas veces al mtodo next() del mismo objeto ge e nerador, la ejecucin del objeto se reinicia en el mismo lugar en el que se o qued (despus del yield anterior) y continua hasta que vuelve a encontrar o e
N.del T.: En espaol yield puede traducirse como ceder. En Python es como si al llegar n la ejecucin de esta sentencia, se cediera el paso devolviendo el valor que aparezca como si fuese o un return pero sin terminar la funcin. La siguiente vez que se ejecute el next() continuar por el o a lugar en el que se ced el paso. o
2

150

CAP ITULO 6. CIERRES Y GENERADORES otra sentencia yield. Todas las variables y, en general, el estado local de la funcin queda almacenado al llegar a la sentencia yield y se recupera al llamar de o nuevo a la funcin next(). La siguiente l o nea de cdigo que se ejecuta llama a o un print() que muestra incrementando x. Despus de eso se ejecuta la sentencia e x = x + 1. Luego se ejecuta el bucle while y lo primero que sucede es que se vuelve a ejecutar yield x, lo que salva el estado en la situacin actual y devuelve o el valor que tiene x (que ahora es 3).

6. Lnea 17: La siguiente vez que llamas a next(counter), sucede lo mismo de nuevo, pero ahora el valor de x es 4. o Puesto que make counter() establece un bucle innito, tericamente se puede ejecutar innitas veces la llamada al mtodo next(), y se mantendr el incremento de e a x y la impresin de sus sucesivos valores. Pero vamos a ser un poco ms productivos o a en el uso de los generadores.

6.7.

Un generador de la serie de Fibonacci


yield pausa la funcin, next() contina desde donde se qued. o u o

1 2 3 4 5

def f i b (max ) : a, b = 0, 1 while a < max : yield a a, b = b, a + b

1. Lnea 2: La serie de Fibonacci es una secuencia nmeros en la que cada uno u de los nmeros es la suma de los dos nmeros anteriores de la serie. Comienu u za en 0 y 1. Va subiendo lentamente al principio, y luego ms rpidamente. a a Para comenzar la secuencia necesitas dos variables: a comienza valiendo 0 y b comienza en 1. 2. Lnea 4: a es el nmero actual de la secuencia, por lo que se puede ceder el u control y retornar dicho valor. 3. Lnea 5: b es el siguiente nmero de la secuencia, por lo que hay que asignarlo u a a, pero tambin hay que calcular antes el nuevo valor siguiente (a + b) y e asignarlo a b para su uso posterior. Observa que esto sucede en paralelo; si a es 3 y b es 5, entonces a, b = b, a + b har que a valga 5 (el valor previo de a b) y b valdr 8 (la suma de los valores previos de a y b). a

6.8. UN GENERADOR DE REGLAS DE PLURALES

151

De este modo tienes una funcin que va retornando los sucesivos nmeros de la o u serie de Fibonacci. Es evidente que tambin podr hacer esto con recursin, pero e as o de este modo es ms fcil de leer. Adems, as tambin es ms fcil trabajar con los a a a e a a bucles for.
1 >>> from f i b o n a c c i import f i b 2 >>> f o r n in f i b ( 1 0 0 0 ) : 3 ... print ( n , end= ) 4 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 5 >>> l i s t ( f i b ( 1 0 0 0 ) ) 6 [ 0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , 55 , 89 , 144 , 233 , 377 , 610 , 987]

1. Lnea 2: Los generadores, como b(), se pueden utilizar directamente en los bucles for. El bucle for llamar automticamente a la funcin next() para rea a o cuperar los valores del generador b() y lo asignar a la variable del a ndice (n). 2. Lnea 3: Cada vez que se pasa por el bucle for, n guarda un nuevo valor obtenido de la sentencia yield de la funcin b(), y todo lo que hay que hacer o en el bucle es imprimirlo. Una vez la funcin b() se queda sin nmeros (a se o u hace mayor que el valor max, que en este caso es 1000), el bucle for termina sin problemas. 3. Lnea 5: Esta es una forma idiomtica de Python muy util: pasar un generador a a la funcin list() que iterar a travs de todo el generador (como en el bucle o a e for del ejemplo anterior) y retorna la lista con todos los valores.

6.8.

Un generador de reglas de plurales

Volvamos al cdigo con las reglas de formacin del plural para ver como funo o ciona plural().
1 2 3 4 5 6 7 8 9 10 11 def r u l e s ( r u l e s f i l e n a m e ) : with open ( r u l e s f i l e n a m e , e n c o d i n g= u t f 8 ) a s p a t t e r n f i l e : f o r l i n e in p a t t e r n f i l e : p a t t e r n , s e a r c h , r e p l a c e = l i n e . s p l i t ( None , 3 ) y i e l d build match and apply functions ( pattern , search , r e p l a c e ) def p l u r a l ( noun , r u l e s f i l e n a m e= p l u r a l 5 r u l e s . t x t ) : f o r m a t c h e s r u l e , a p p l y r u l e in r u l e s ( r u l e s f i l e n a m e ) : i f m a t c h e s r u l e ( noun ) : return a p p l y r u l e ( noun ) r a i s e V a l u e E r r o r ( no matching r u l e f o r {0} . format ( noun ) )

152

CAP ITULO 6. CIERRES Y GENERADORES

1. Lnea 4: No hay ninguna magia aqu Recuerda que las l . neas del chero de reglas tienen tres valores separados por espacios en blanco, por lo que utilizas line.split(None, 3) para retornar las tres columnas y asignarlas a las tres variables locales. 2. Lnea 5: Y lo siguiente es un yield. Qu es lo que se cede? las dos funciones que e se construyen dinmicamente con la ya conocida build match and apply functions, a que es idntica a los ejemplos anteriores. En otras palabras, rules() es un genee rador que va retornando funciones de bsqueda y sustitucin bajo demanda. u o 3. Lnea 8: Puesto que rules() es un generador, puedes utilizarlo directamente en un bucle for. La primera vez, el bucle for llama a la funcin rules(), que abre o el chero de patrones, lee la primera l nea, construye de forma dinmica las a funciones de bsqueda y sustitucin de los patrones de esa l u o nea y cede el control devolviendo ambas funciones. La segunda vuelta en el bucle for contina u en el lugar exacto en el que se cedi el control por parte del generador rules(). o Lo primero que har es leer la siguiente l a nea del chero (que contina abieru to), construir dinmicamente otras dos funciones de bsqueda y sustitucin a a u o basadas en los patrones de esa l nea del chero y, de nuevo, ceder el control a devolviendo las dos nuevas funciones. Qu es lo que hemos ganado con respecto de la versin anterior? Tiempo e o de inicio. En la versin anterior, cuando se importaba el mdulo, se le el chero o o a completo y se constru una lista con todas las reglas posibles. Todo ello, antes de a que se pudiera ejecuta la funcin plural(). Con los generadores, puedes hacero todo de o forma perezosa: primero lees la primera regla, creas las funciones y las pruebas, si con ello se pasa el nombre a plural, no es necesario seguir leyendo el resto del chero, ni crear otras funciones. Qu se pierde? Rendimiento! Cada vez que llamas a la funcin plural(), el e o generador rules() vuelve a iniciarse desde el principio (se genera un nuevo objeto cada vez que se llama a la funcin generador rules()) lo que signica que se reabre o el chero de patrones y se lee desde el comienzo, una l nea cada vez. Cmo podr o amos tener lo mejor de los dos mundos?: coste m nimo de inicio (sin ejecutar ningn cdigo en el import), y mximo rendimiento (sin construir las u o a mismas funciones una y otra vez). Ah! y todo ello manteniendo las reglas en un chero separado (porque el cdigo es cdigo y los datos son datos) de forma que no o o haya que leer la misma l nea del chero dos veces. Para hacer eso, necesitas construir un iterador. Pero antes de hacerlo, es necesario que aprendas a manejar las clases de objetos en Python.

6.9. LECTURAS RECOMENDADAS

153

6.9.

Lecturas recomendadas

Generadores simples (PEP 255): http://www.python.org/dev/peps/pep-0255/ Comprendiendo la sentencia with de Python: http://ebot.org/zone/pythonwith-statement.htm Cierres en Python: http://ynniv.com/blog/2007/08/closures-in-python.html Nmeros de Fibonacci: http://en.wikipedia.org/wiki/Fibonacci number u Nombres plurales irregulares en ingls: e http://www2.gsu.edu/wwwesl/egw/crump.htm

154

CAP ITULO 6. CIERRES Y GENERADORES

Cap tulo 7 Clases e iteradores


Nivel de dicultad:   El Este est al Este y el Oeste al Oeste, a y nunca ambos se encontrarn. a Ruyard Kipling

7.1.

Inmersin o

Los generadores son unicamente un caso especial de iteradores. Una funcin o que entrega valores es una forma buena de crear un iterador sin llegar a crearlo. Djame ensearte lo que quiero decir. e n Recuerdas el generador de la serie de Fibonacci? Aqu lo tienes construido como un iterador:

155

156
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

CAP ITULO 7. CLASES E ITERADORES

c l a s s Fib : i t e r a d o r que g e n e r a l o s n meros de l a s e c u e n c i a de F i b o n a c c i u def i n i t ( s e l f , max ) : s e l f . max = max iter ( self ): self .a = 0 self .b = 1 return s e l f next ( self ): fib = self .a i f f i b > s e l f . max : raise StopIteration self .a, self .b = self .b, self .a + self .b return f i b

def

def

Vamos a ver una l nea cada vez.


1 c l a s s Fib :

class? Qu es una clase? e

7.2.

Cmo se denen clases o

Python es un lenguaje orientado a objetos: Puedes denir tus propias clases de objetos, heredar de tus clases o de las preexistentes y crear instancias de las clases que has denido. Es sencillo denir una clase en Python. Como con las funciones no existe una denicin del interface separada. Simplemente dene la clase y el cdigo. Una clase o o en Python comienza con la palabra reservada class, seguida del nombre de la clase. Tcnicamente es todo lo que se necesita puesto que no necesita heredar de ninguna e otra.
1 2 c l a s s PapayaWhip : pass

1. Lnea 1: El nombre de esta clase es PapayaWhip y no hereda de ninguna otra. Los nombres de las clases se suelen escribir con el primer carcter en mayscua u las, CadaPalabraDeEstaForma pero esto es unicamente por convencin, no es o un requisito obligatorio. 2. Lnea 2: Probablemente lo hasta adivinado, pero el contenido de la clase

7.2. COMO SE DEFINEN CLASES

157

est siempre indentado, como pasa con el cdigo de una funcin, una sena o o tencia if, un bucle for o cualquier otro bloque de cdigo. La primera l o nea no indentada indica el nal de la clase y se encuentra fuera de la misma. Esta clase PapayaWhip no dene ningn mtodo o atributos, pero es correcta u e sintcticamente. Como necesita que exista algo en el contenido de la clase se escribe a la sentencia pass. Esta palabra reservada de Python signica unicamente sigue adelante, no hay nada que hacer aqu Es una palabra reservada que no hace nada . y, por ello, una buena forma de marcar un sitio cuando tienes funciones o clases a medio escribir. La sentencia pass de Python es como una pareja vac de llaves ({}) en a Java o C. Muchas clases heredan de otras, pero esta no. Muchas clases denen mtodos, e pero esta no. No hay nada que tenga que tener obligatoriamente una clase de Python, salvo el nombre. En particular, los programadores de C++ pueden encontrar extrao n que las clases de Python no tengan que tener constructores y destructores explicitos. Aunque no es obligatorio, las clases de Python pueden tener algo parecido a un constructor: el mtodo init (). e

7.2.1.

El mtodo e

init ()

Este ejemplo muestra la inicializacion de la clase Fib utilizando el mtodo e init ().
1 2 3 4 c l a s s Fib : i t e r a d o r que g e n e r a l o s n meros de l a s e c u e n c i a de F i b o n a c c i u def init ( s e l f , max ) :

1. Lnea 2: Las clases pueden (y deber an) tener docstrings, tal y como sucede con los mdulos y funciones. o 2. Lnea 4: El mtodo init () se llama de forma automtica por Python in e a mediatamente despus de que se haya creado una instancia de la clase. Es e tentador pero tcnicamente incorrecto llamar a este mtodo el construce e tor de la clase. Es tentador, porque recuerda a los constructores de C++ (por convencin, el mtodo init () se suele poner como el primer mtodo de o e e la clase), e incluso suena como uno. Es incorrecto, porque cuando se llama a este mtodo en Python, el objeto ya ha sido construido y ya dispones de una e referencia a una instancia vlida de la clase (self). a

158

CAP ITULO 7. CLASES E ITERADORES

El primer parmetro de todo mtodo de una clase, incluyendo el mtodo a e e init (), siempre es una referencia al objeto actual, a la instancia actual de la clase. Por convencin, este parmetro se suele llamar self. Este parmetro ocupa el rol de o a a la palabra reservada this de C++ o Java, pero self no es una palabra reservada en Python, es simplemente una convencin en para el nombre del primer parmetro de o a los mtodos de una clase. En cualquier caso, por favor no lo llames de otra forma e que no sea self; esta convencin es muy fuerte y todo el mundo la usa. o e e En el mtodo init (), self se reere al objeto recin creado; en otros mtodos e de la clase se reere a la instancia cuyo mtodo se llam. Aunque necesitas especicar e o self expl citamente cuando denes el mtodo, no lo especicas cuando se llama. e Python lo hace por ti automticamente. a

7.3.

Instanciar clases

Instanciar clases en Python es inmediato. Para crear un objeto de la clase, simplemente llama a la clase como si fuese una funcin, pasndole los parmetros o a a que requiera el mtodo init (). El valor de retorno ser el nuevo objeto. e a
1 2 3 4 5 6 7 8 >>> import f i b o n a c c i 2 >>> f i b = f i b o n a c c i 2 . Fib ( 1 0 0 ) >>> f i b <f i b o n a c c i 2 . Fib o b j e c t a t 0x00DB8810> >>> f i b . c l a s s <c l a s s f i b o n a c c i 2 . Fib > >>> f i b . d o c i t e r a d o r que g e n e r a l o s n meros de l a s e c u e n c i a de F i b o n a c c i u

1. Lnea 2: Se crea una instancia de la clase Fib (denida en el mdulo bonacci2) o y se asigna la instancia creada a la variable b. Se pasa un parmetro que ser el a a parmetro max del mtodo init () de la clase Fib. a e 2. Lnea 3: La variable b se reere ahora a un objeto que es instancia de la clase Fib. 3. Lnea 5: Toda instancia de una clase tiene el atributo interno class que es la clase del objeto. Muchos programadores java estarn familiarizados con la clase a Class, que contiene mtodos como getName() y getSuperClass() para conseguir e informacin de metadatos del objeto. En Python, esta clase de metadados o est disponible mediante el uso de atributos, pero la idea es la misma. a 4. Lnea 7: Puedes acceder al docstring de la instancia como se hace con cualquier

7.4. VARIABLES DE LAS INSTANCIAS

159

otro mdulo o funcin. Todos los objetos que son instancia de una misma clase o o comparten el mismo docstring En Python, basta con llamar a una clase como si fuese una funcin para o crear un nuevo objeto de la clase. No existe ningn operador new como u sucede en C++ o Java.

7.4.

Variables de las instancias

En el siguiente cdigo: o
1 2 3 c l a s s Fib : def i n i t ( s e l f , max ) : s e l f . max = max

1. Lnea 3: Qu es self.max? Es una variable de la instancia. Completamente e diferente al parmetro max, que se pasa como parmetro del mtodo. self.max a a e es una variable del objeto creado. Lo que signica que puedes acceder a ella desde otros mtodos. e
1 2 3 4 5 6 7 8 9 c l a s s Fib : def i n i t ( s e l f , max ) : s e l f . max = max . . . def next ( self ): fib = self .a i f f i b > s e l f . max :

1. Lnea 3: self.max se crea en el mtodo init (), por ser el primero que se llama. e 2. Lnea 9: ...y se utiliza en el mtodo e next ().

Las variables de instancia son espec cas de cada objeto de la clase. Por ejemplo, si creas dos objetos Fib con diferentes valores mximos cada uno de ellos recora dar sus propios valores. a

160
1 2 3 4 5 6 7 >>> >>> >>> >>> 100 >>> 200 import f i b o n a c c i 2 f i b 1 = f i b o n a c c i 2 . Fib ( 1 0 0 ) f i b 2 = f i b o n a c c i 2 . Fib ( 2 0 0 ) f i b 1 . max f i b 2 . max

CAP ITULO 7. CLASES E ITERADORES

7.5.

Un iterador para la serie de Fibonacci

Ahora ests preparado para aprender cmo construir un iterador. Un iterador a o es una clase que dene el mtodo iter (). e Los tres mtodos de clase, init , iter y next , comienzan y termie e a nan con un par de guiones bajos ( ). Porqu? No es nada mgico, pero habitualmente signica que son mtodos especiales. Lo unico que tienen e en especial estos mtodos especiales es que no se llaman directamente; e Python los llama cuando utilizas otra sintaxis en la clase o en una instancia de la clase.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 c l a s s Fib : def i n i t ( s e l f , max ) : s e l f . max = max def iter ( self ): self .a = 0 self .b = 1 return s e l f next ( self ): fib = self .a i f f i b > s e l f . max : raise StopIteration self .a, self .b = self .b, self .a + self .b return f i b

def

1. Lnea 1: Para poder construir un iterador desde cero b necesita ser una clase, no una funcin. o 2. Lnea 2: Al llamar a Fib(max) se est creando un objeto que es instancia de a esta clase y llamndose a su mtodo init () con el parmetro max. El mtodo a e a e init () guarda el valor mximo como una variable del objeto de forma que a los otros mtodos de la instancia puedan utilizarlo ms tarde. e a

7.5. UN ITERADOR PARA LA SERIE DE FIBONACCI

161

3. Lnea 5: El mtodo iter () se llama siempre que alguien llama a iter(b) (Co e mo vers en un minuo, el bucle for llamar a este mtodo automticamente, a a e a pero t tambin puedes llamarlo manualmente). Despus de efectuar la iniciau e e lizacin necesaria de comienzo de la iteracin (en este caso inicializar self.a y o o self.b) el mtodo iter () puede retornar cualquier objeto que implemente el e a), iter () se limita a devolver mtodo next (). En este caso (y en la mayor e self, puesto que la propia clase implementa el mtodo next (). e 4. Lnea 10: El mtodo next () se llama siempre que alguien llame al mtodo e e next() sobre un iterador de una instancia de una clase. Esto adquirir todo su a sentido en un minuto. o 5. Lnea 13: Cuando el mtodo next () lanza la excepcin StopIteration le e est indicando a quin lo haya llamado que el iterador se ha agotado. Al a e contrario que la mayor de las excepciones, no se trata de un error. es una a condicin normal que simplemente signica que no quedan ms valores que o a generar. Si el llamante es un bucle for se dar cuenta de que se ha elevado a esta excepcin y nalizar el bucle sin que se produzca ningn error (En otras o a u palabrs, se tragar la excepcin). Esta pequea magia es el secreto clave del a a o n uso de iteradores en los bucles for. 6. Lnea 15: Para devolver el siguiente valor del iterador, el mtodo next () e simplemente utiliza return para devolver el valor. No se utiliza yield, que uni camente se utiliza para los generadores. Cuando se est creando un iterador a desde cero, se utiliza return en el mtodo next (). e Ests ya totalmente confundido? Excelente. Veamos cmo utilizar el iterador. a o
1 >>> from f i b o n a c c i 2 import Fib 2 >>> f o r n in Fib ( 1 0 0 0 ) : 3 ... print ( n , end= ) 4 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987

Exactamente igual que en el ejemplo del generador! Es idntico a como usabas e el generador. Pero... cmo?. o Existe algo de trabajo ejecutndose en los bucles for. a El bucle for llama a Fib(1000), que retorna un objeto que es instancia de la clase Fib. Llammoslo b inst. e En secreto, pero de forma inteligente, el bucle for llama a iter(b inst), que retorna un objeto iterador. Vamos a llamar a este objeto b iter. En este caso, b iter == b inst, porque el mtodo b inst. iter () retorna self, pero el bucle e for no lo sabe, ni le importa.

162

CAP ITULO 7. CLASES E ITERADORES Para recorrer el bucle a travs del iterador, el bucle for llama a next(b iter), e que, a su vez, llama a b iter. next (), el mtodo next () del objeto b iter, e que realiza los clculos necesarios y devuelve el siguiente elemento de la serie a de bonacci. El bucle for toma este valor y lo asigna a n, luego ejecuta el cuerpo del bucle para ese valor de n. Cmo sabe el bucle for cuando parar? Me alegro de que lo preguntes! Cuando o o next(b iter) eleva la excepcin StopIteration el bucle for la captura naliza sin error (Cualquier otra excepcin se elevar normalmente). Y dnde has o a o visto que se lanze esta excepcin StopIteration? En el mtodo next () Por o e supuesto!

7.6.

Un iterador para reglas de formacin de pluo rales

Ahora es el nal. Vamos a reescribir el generador de reglas de formacin de o plural como un iterador. iter(f) llama a f. iter (). next(f) llama a f. next ().

7.6. UN ITERADOR PARA REGLAS DE FORMACION DE PLURALES


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 c l a s s LazyRules : r u l e s f i l e n a m e = p l u r a l 6 r u l e s . t x t def

163

init ( self ): s e l f . p a t t e r n f i l e = open ( s e l f . r u l e s f i l e n a m e , e n c o d i n g= u t f 8 ) s e l f . cache = [ ] iter ( self ): s e l f . cache index = 0 return s e l f next ( self ): s e l f . c a c h e i n d e x += 1 i f l e n ( s e l f . c a c h e ) >= s e l f . c a c h e i n d e x : return s e l f . c a c h e [ s e l f . c a c h e i n d e x 1 ] if s e l f . pattern file . closed : raise StopIteration line = s e l f . pattern file . readline () i f not l i n e : s e l f . pattern file . close () raise StopIteration p a t t e r n , s e a r c h , r e p l a c e = l i n e . s p l i t ( None , 3 ) funcs = build match and apply functions ( pattern , search , r e p l a c e ) s e l f . c a c h e . append ( f u n c s ) return f u n c s

def

def

r u l e s = LazyRules ( )

Como esta clase implementa los mtodos iter () y next () puede utilizarse e como un iterador. Al nal del cdigo se crea una instancia de la clase y se asigna a o la variable rules. Vamos a ver la clase poco a poco.
1 2 3 4 5 6 c l a s s LazyRules : r u l e s f i l e n a m e = p l u r a l 6 r u l e s . t x t def init ( self ): s e l f . p a t t e r n f i l e = open ( s e l f . r u l e s f i l e n a m e , e n c o d i n g= u t f 8 ) s e l f . cache = [ ]

1. Lnea 5: Cuando instanciamos la clase LazyRules, se abre el chero de patrones pero no se lee nada de l (Eso se hace ms tarde). e a

164

CAP ITULO 7. CLASES E ITERADORES

2. Lnea 6: Despus de abrir el chero de patrones se inicializa la cach. Utilizars e e a la cach ms tarde (en el mtodo next ()) segn se lean las las del chero e a e u de patrones. a Antes de continuar vamos a echarle un vistazo a rules lename. No est denida en el mtodo iter (). De hecho no est denida dentro de ningn mtodo. e a u e Est denida al nivel de la clase. Es una variable de clase y, aunque puedes acceder a a ella igual que a cualquier variable de instancia (self.rules lename), es compartida en todas las instancias de la clase LazyRules.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 >>> import p l u r a l 6 >>> r 1 = p l u r a l 6 . LazyRules ( ) >>> r 2 = p l u r a l 6 . LazyRules ( ) >>> r 1 . r u l e s f i l e n a m e p l u r a l 6 r u l e s . t x t >>> r 2 . r u l e s f i l e n a m e p l u r a l 6 r u l e s . t x t >>> r 2 . r u l e s f i l e n a m e = r2 o v e r r i d e . t x t >>> r 2 . r u l e s f i l e n a m e r2 o v e r r i d e . t x t >>> r 1 . r u l e s f i l e n a m e p l u r a l 6 r u l e s . t x t >>> r 2 . c l a s s . r u l e s f i l e n a m e p l u r a l 6 r u l e s . t x t >>> r 2 . c l a s s . r u l e s f i l e n a m e = papayawhip . t x t >>> r 1 . r u l e s f i l e n a m e papayawhip . t x t >>> r 2 . r u l e s f i l e n a m e r2 o v e r r i d e t x t

1. Lnea 4: Cada instancia de la clase hereda el atributo rules lename con el valor denido para la clase. 2. Lnea 8: La modicacin del valor de la variable en una instancia no afecta al o valor de las otras instancias... 3. Lnea 13: ...ni cambia el atributo de la clase. Puedes acceder al atributo de la clase (por oposicin al atributo de la instancia individual) mediante el uso del o atributo especial class que accede a la clase. 4. Lnea 15: Si modicas el atributo de la clase, todas las instancias que heredan ese atributo (como r1 en este ejemplo) se vern afectadas. a 5. Lnea 18: Todas las instancias que han modicado ese atributo, sustituyendo su valor (como r2 aqu no se vern afectadas. ) a

7.6. UN ITERADOR PARA REGLAS DE FORMACION DE PLURALES Y ahora volvamos a nuestro espectculo. a
1 2 3 def iter ( self ): s e l f . cache index = 0 return s e l f

165

1. Lnea 1: El mtodo iter () se llamar cada vez que alguien digamos un e a bucle for llame a iter(rules). 2. Lnea 3: Todo mtodo iter () debe devolver un iterador. En este caso devuel e ve self puesto que esta clase dene un mtodo next () que ser responsable e a de devolver los diferentes valores durante las iteraciones.
1 2 3 4 5 6 7 8 9 def next ( self ): . . . p a t t e r n , s e a r c h , r e p l a c e = l i n e . s p l i t ( None , 3 ) funcs = build match and apply functions ( pattern , search , r e p l a c e ) s e l f . c a c h e . append ( f u n c s ) return f u n c s

1. Lnea 1: El mtodo next () se llama cuando alguien digamos que un bucle e for llama a next(rules). La mejor forma de explicar este mtodo es comene zando del nal hacia atrs. Por lo que vamos a hacer eso. a 2. Lnea 6: La ultima parte de esta funcin deber serte familiar. La funcin o a o build match and apply functions() no ha cambiado; es igual que siempre. 3. Lnea 8: La unica diferencia es que, antes de retornar el valor (que se almacena en la tupla funcs), vamos a salvarlas en self.cache. Sigamos viendo la parte anterior...
1 2 3 4 5 6 7 8 9 10 11 def next ( self ): . . . line = s e l f . pattern file . readline () i f not l i n e : s e l f . pattern file . close () raise StopIteration . . .

166

CAP ITULO 7. CLASES E ITERADORES

1. Lnea 5: Veamos una tcnica avanzada de acceso a cheros. El mtodo read e e line() (nota: singular, no el plural readlines()) que lee exactamente una l nea de un chero abierto. Espec camente, la siguiente l nea (Los objetos chero tambin son iteradores...). e 2. Lnea 6: Si el mtodo readline() lee algo (quedaban l e neas por leer en el chero), la variable line no ser vac Incluso si la l a a. nea fuese vac la variable a, contendr una cadena de un carcter a a n (el retorno de carro). Si la variable line es realmente una cadena vac siga nicar que no quedan l a neas por leer en el chero. 3. Lnea 8: Cuando alcanzamos el nal del chero deber amos cerrarlo y elevar la excepcin mgica StopIteration. Recuerda que llegamos a esta parte de la o a funcin porque necesitamos encontrar una funcin de bsqueda y otra de sustio o u tucin para la siguiente regla. La siguiente regla tiene que venir en la siguiente o l nea del chero... pero si no hay l nea siguiente! Entonces, no tenemos que retornar ningn valor. Las iteraciones han terminado (Se acab la esta...). u o Si seguimos movindonos hacia el comienzo del mtodo e e
1 2 3 4 5 6 7 8 9 10 def next ( self ): s e l f . c a c h e i n d e x += 1 i f l e n ( s e l f . c a c h e ) >= s e l f . c a c h e i n d e x : return s e l f . c a c h e [ s e l f . c a c h e i n d e x 1 ] if s e l f . pattern file . closed : raise StopIteration . . .

next ()...

1. Lnea 4: self.cache contendr una lista con las funciones que necesitamos para a buscar y aplicar las diferentes reglas (Al menos esto te deber resultar famia ndice del elemento de la cach que se debe e liar!). self.cache index mantiene el retornar. Si no hemos consumido toda la cach (si la longitud de self.cache es e mayor que self.cache index), tenemos un elemento en la cach para retornar! e Podemos devolver las funciones de bsqueda y sustitucin de la cach en lugar u o e de construirlas desde cero. 2. Lnea 7: Por otra parte, si no obtenemos un elemento de la cach y el chero e se ha cerrado (lo que podr haber sucedido en la parte de ms abajo del a a mtodo, como se vio anteriormente) entonces ya no hay nada ms que hacer. e a Si el chero se ha cerrado, signica que lo hemos leido completamente ya

7.6. UN ITERADOR PARA REGLAS DE FORMACION DE PLURALES

167

hemos recorrido todas las l neas del chero de patrones y hemos construido y cacheado las funciones de bsqueda y sustitucin de cada patrn. El chero u o o se ha agotado y la cach tambin, U! yo tambin estoy agotado! Espera un e e e momento, casi hemos terminado. Si lo ponemos todo junto esto es lo que sucede cuando: Cuando el mdulo es importado crea una unica instancia de la clase LazyRules, o que denominamos rules, que abre el chero de patrones pero no lo lee. Cuando pedimos la primera pareja de funciones de bsqueda y sustitucin, u o busca en la cach pero est vac Por lo que lee una l e a a. nea del chero de patrones, construye la pareja de funciones de bsqueda y sustitucin para u o ellas y las guarda en la cach (adems de retornarlas). e a Digamos, por simplicidad, que la primera regla coincidi con la bsqueda. Si o u es as no se busca ndad ms y no se lee nada ms del chero de patrones. a a Adems, por continuar con el argumento, supn que el programa que est usana o a do este objeto llama a la funcin plural() de nuevo para pasar al plural una pao labra diferente. El bucle for de la funcin plural() llamar a la funcin iter(rules), o a o que resetea el ndice de la cach pero no resetea el chero abierto. e La primera vez en esta nueva iteracin, el bucle for pedir el valor de rules, o a e que llama al mtodo next (). Esta vez, sin embargo, la cach tiene ya una e pareja de funciones de bsqueda y sustitucin, la correspondiente a los patrones u o de la primera l nea del chero de patrones, puesto que fueron construidas y cacheadas al generar el plural de la palabra anterior y por eso estn en la a cach. El e ndice de la cach se incrementa y no se toca el chero abierto. e Vamos a decir, en aras de continuar el argumento, que esta vez la primera regla no coincide, por lo que el bucle for da otra vuelta y pide otro valor de la variable rules. Por lo que se invoca por segunda vez al mtodo next (). e Esta vez la cach est agotada solamente conten un elemento, y estamos e a a solicitando un segundo por lo que el mtodo next () contina. Se lee otra e u l nea del chero abierto, se construyen las funciones de bsqueda y sustitucin u o de los patrones y se introducen en la cach. e Este proceso de lectura, construccin y cach continua mientras las reglas del o e chero no coincidan con la palabra que estamos intentando poner en plural. Si se llega a encontrar una coincidencia antes del nal del chero, se utiliza y termina, con el chero an abierto. El puntero del chero permanecer dondeu a quiera que se parase la lectura, a la espera de la siguiente sentencia readline().

168

CAP ITULO 7. CLASES E ITERADORES Mientras tanto, la cach ha ido ocupndose con ms elementos y si se volviera e a a a intentar poner en plural a otra palabra, se probar primero con los elementos a de la cach antes de intentar leer la siguiente l e nea del chero de patrones. Hemos alcanzado el nirvana de la generacin de plurales. o

1. Coste de inicio m nimo. Lo unico que se hace al realizar el import es ins tanciar un objeto de una clase y abrir un chero (pero sin leer de l). e 2. Mximo rendimiento. El ejemplo anterior leer el chero cada vez que a a hubiera que poner en plural una palabra. Esta versin cachea las funciones o segn se van construyendo y, en el peor caso, solamente leer del chero de u a patrones una unica vez, no importa cuantas palabras pongas en plural. 3. Separacin de cdigo y datos. Todos los patrones se almacenan en un o o chero separado. El cdigo es cdigo y los datos son datos y nunca se debern o o a de encontrar. Es realmente el nirvana? Bueno, s y no. Hay algo que hay que tener en cuenta con el ejemplo de LazyRules: El chero de patrones se abre en el mtodo init () y permanece abierto hasta que se alcanza la regla nal. e Python cerrar el chero cuando se acabe la ejecucin, o despus de que a o e la ultima instancia de la clase LazyRules sea destruida, pero eso puede ser mucho tiempo. Si esta clase es parte de un proceso de larga duracin, o el intrprete de Python puede que no acabe nunca y el objeto LazyRules e puede que nunca sea destruido. Hay formas de evitar esto. En lugar de abrir el chero durante el mtodo e init () y dejarlo abierto mientras lees las reglas una l nea cada vez, podr abrir el as chero, leer todas las reglas y cerrarlo inmediatamente. O podr abrir el chero, leer as una regla, guardar la posicin del chero con el mtodo tell(), cerrar el chero y, ms o e a tarde, reabrirlo y utilizar el mtodo seek() para continuar leyendo donde lo dejaste. e O podr no preocuparte de dejar el chero abierto, como pasa en este ejemplo. as Programar es disear, y disear es siempre una continua eleccin entre decisiones n n o que presentan ventajas e inconvenientes. Dejar un chero abierto demasiado tiempo puede suponer un problema; hacer el cdigo demasiado complejo podr ser un o a problema. Cul es el problema mayor depende del equipo de desarrollo, la aplicacin a o y tu entorno de ejecucin. o

7.7. LECTURAS RECOMENDADAS

169

7.7.

Lecturas recomendadas

Tipos de iteradores: http://docs.python.org/3.1/library/stdtypes.html#iteratortypes PEP 234: Iteradores: http://www.python.org/dev/peps/pep-0234/ PEP 255: Generadores simples: http://www.python.org/dev/peps/pep-0255/ Tcnicas de generadores para programadores de sistemas: e http://www.dabeaz.com/generators/

170

CAP ITULO 7. CLASES E ITERADORES

Cap tulo 8 Iteradores avanzados


Nivel de dicultad:  Las pulgas grandes tienen pulgas ms pequeas sobre sus espaldas que les pican a n y las pulgas pequeas tienen pulgas an ms pequeas, y as hasta el innito. n u a n August de Morgan

8.1.

Inmersin o

Hawaii + idaho + iowa + ohio == states. O, por ponerlo de otra manera, 510199 + 98153 + 9301 + 3593 == 621246. Estoy hablando en clave? No, solamente es un rompecabezas. Djame aclarrtelo. e a
1 2 3 4 5 6 7 8 9 10 11 12 HAWAII + IDAHO + IOWA + OHIO == STATES 510199 + 98153 + 9301 + 3593 == 621246 H = A = W= I = D = O = S = T = E = 5 1 0 9 8 3 6 2 4

Las letras forman palabras existentes, pero si sustituyes cada palabra por un d gito del 0 a 9, tambin forman una equacin aritmtica. El truco consiste en e o e 171

172

CAP ITULO 8. ITERADORES AVANZADOS

descubrir cmo se emparejan letras y d o gitos. Todas las apariciones de una misma letra deben sustituirse por el mismo d gito, no de puede repetir ningn d u gito y ninguna palabra debe comenzar por el d gito 0. El rompecabezas ms conocido de este tipo es: SEND + MORE = MONEY. a A este tipo de rompecabezas se les llama alfamticos o criptaritmos. En ese te capitulo nos sumergiremos en un incre programa escrito originariamente por ble Raymond Hettinger. Este programa resuelve este tipo de rompecabezas en unica mente 14 lneas de cdigo. o
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import r e import i t e r t o o l s def s o l v e ( p u z z l e ) : words = r e . f i n d a l l ( [ AZ]+ , p u z z l e . upper ( ) ) u n i q u e c h a r a c t e r s = s e t ( . j o i n ( words ) ) a s s e r t l e n ( u n i q u e c h a r a c t e r s ) <= 1 0 , Demasiadas l e t r a s f i r s t l e t t e r s = {word [ 0 ] f o r word in words } n = len ( f i r s t l e t t e r s ) sorted characters = . join ( f i r s t l e t t e r s ) + \ . join ( unique characters f i r s t l e t t e r s ) c h a r a c t e r s = t u p l e ( ord ( c ) f o r c in s o r t e d c h a r a c t e r s ) d i g i t s = t u p l e ( ord ( c ) f o r c in 0123456789 ) zero = d i g i t s [ 0 ] f o r g u e s s in i t e r t o o l s . p e r m u t a t i o n s ( d i g i t s , l e n ( c h a r a c t e r s ) ) : i f z e r o not in g u e s s [ : n ] : equation = puzzle . t r a n s l a t e ( dict ( zip ( characters , guess ) ) ) i f eval ( equation ) : return e q u a t i o n if name == m a i n : import s y s f o r p u z z l e in s y s . argv [ 1 : ] : print ( p u z z l e ) solution = solve ( puzzle ) if solution : print ( s o l u t i o n )

Puedes ejecutar este programa desde la l nea de comando. En Linux ser as a . (Puede tardar un poco, dependiendo de la velocidad de tu ordenador, y no hay barra de progreso, s paciente!) e

8.2. ENCONTRAR TODAS LAS OCURRENCIAS DE UN PATRON


1 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 a l p h a m e t i c s . py 2 HAWAII + IDAHO + IOWA + OHIO == STATES 3 HAWAII + IDAHO + IOWA + OHIO = STATES 4 510199 + 98153 + 9301 + 3593 == 621246 5 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 a l p h a m e t i c s . py 6 I + LOVE + YOU == DORA 7 I + LOVE + YOU == DORA 8 1 + 2784 + 975 == 3760 9 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 a l p h a m e t i c s . py 10 SEND + MORE == MONEY 11 SEND + MORE == MONEY 12 9567 + 1085 == 10652

173

8.2.

Encontrar todas las ocurrencias de un patrn o

Lo primero que este solucionador de rompecabezas hace es encontrar todas las letras de la A a la Z del puzzle.
1 >>> import r e 2 >>> r e . f i n d a l l ( 3 [ 16 , 2 , 4 4 >>> r e . f i n d a l l ( 5 [ SEND , MORE [0 9]+ , 16 2by 4s i n rows o f 8 ) , 8 ] [ AZ]+ , SEND + MORE == MONEY ) , MONEY ]

1. Lnea 2: El mdulo re contiene la implementacin de Python de las expresiones o o regulares. Tiene una funcin muy simple de usar denominada ndall() que o toma un patrn de expresin regular y una cadena y encuentra todas las o o ocurrencias del patrn en la cadena. En este caso, el patrn debe coincidir o o con una secuencia de nmeros. La funcin ndall() devuelve una lista de las u o subcadenas que han coincidido con el patrn. o 2. Lnea 4: En este caso el patrn de la expresin regular coincide con una se o o cuencia de letras. De nuevo, el valor de retorno es una lista y cada elemento de la lista es una cadena que representa una ocurrencia del patrn en la cadena o original. Aqu hay otro ejemplo que te servir para forzar tu cerebro un poco. a
1 >>> r e . f i n d a l l ( s . ? s , The s i x t h s i c k s h e i k h s s i x t h s h e e p s s i c k . ) 2 [ s i x t h s , sheikh s s , sheep s s ]

174

CAP ITULO 8. ITERADORES AVANZADOS

Sorprendido? La expresin regular busca o Este es el trabalenguas ms a un espacio, una letra s, y luego la serie de caacdif en el idioma ingls. cil e teres ms corta posible formada por cualquier a carcter (.*?), luego un espacio y luego otra s. a Bien, echando un vistazo a la cadena de entrada vemos cinco coincidencias. 1. The sixth sick sheikhs sixth sheeps sick. 2. The sixth sick sheikhs sixth sheeps sick. 3. The sixth sick sheikhs sixth sheeps sick. 4. The sixth sick sheikhs sixth sheeps sick. 5. The sixth sick sheikhs sixth sheeps sick. Pero la funcin re.ndall() solamente devolvi tres coincidencias. En concreto, o o la primera, la tercera y la quinta. Porqu? porque no devuelve coincidencias soe lapadas. La primera coincidencia se solapa con la segunda, por lo que se devuelve la primera y la segunda se salta. Luego la tercera se solapa con la cuarta, por lo que se devuelve la tercera y la cuarta se salta. Finalmente se devuelve la quinta coincidencia. Tres coincidencias, no cinco. Esto no tiene nada que ver con el solucionador de rompecabezas alfamticos, e simplemente pens que era interesante. e

8.3.

Encontrar los elementos unicos de una se cuencia

Los conjuntos hacen que esta tarea sea trivial.


1 2 3 4 5 6 7 8 9 10 11 >>> a l i s t = [ The , s i x t h , s i c k , s h e i k s , s i x t h , s h e e p s , s i c k ] >>> s e t ( a l i s t ) { s i x t h , The , s h e e p s , s i c k , s h e i k s } >>> a s t r i n g = EAST IS EAST >>> s e t ( a s t r i n g ) { A , , E , I , S , T } >>> words = [ SEND , MORE , MONEY ] >>> . j o i n ( words ) SENDMOREMONEY >>> s e t ( . j o i n ( words ) ) { E , D , M , O , N , S , R , Y }

8.4. HACER ASERCIONES

175

1. Lnea 2: Dada una lista de varias cadenas, la funcion set() devolver un con a junto de cadenas unicas de la lista. Esto cobra sentido si piensas en ello como si fuese un bucle for. Toma el primer elemento de la lista y lo pone en el conjunto. Luego el segundo, tercero, cuarto, quinto un momento, este ya est en el conjunto, por lo que no se vuelve a incluir, porque los conjuntos de a Python no permiten duplicados. El sexto, sptimo de nuevo un duplicado, e por lo que se no se vuelve a incluir. El resultado nal? Todos los elementos sin repeticiones de la lista original. La lista original ni siquiera necesita estar ordenada primero. 2. Lnea 5: La misma tcnica funciona con cadenas, puesto que una cadena es e simplemente una secuencia de caracteres. 3. Lnea 8: Dada una lista de cadenas, .join(a list) concatena todas las cadenas en una unica cadena. 4. Lnea 10: Dada una cadena (secuencia de caracteres -al usar la funcin join-), o esta l nea de cdigo retorna todos los caracteres sin duplicados. o El solucionador de rompecabezas alfamticos utiliza esta tcnica para construir e e un conjunto con todos los caracteres, sin repeticiones, existentes en el rompecabezas.
1 u n i q u e c h a r a c t e r s = s e t ( . j o i n ( words ) )

Esta lista se utiliza despus para asignar d e gitos a los caracteres segn el soluu cionador itera a travs de las posibles soluciones. e

8.4.

Hacer aserciones

Como en muchos lenguajes de programacin, en Python existe la sentencia o assert. Veamos cmo funciona. o
1 >>> a s s e r t 1 + 1 == 2 2 >>> a s s e r t 1 + 1 == 3 3 Traceback ( most r e c e n t c a l l l a s t ) : 4 F i l e <s t d i n > , l i n e 1 , in <module> 5 AssertionError 6 >>> a s s e r t 2 + 2 == 5 , Solamente para v a l o r e s muy g r a n d e s de 2 7 Traceback ( most r e c e n t c a l l l a s t ) : 8 F i l e <s t d i n > , l i n e 1 , in <module> 9 A s s e r t i o n E r r o r : Only f o r v e r y l a r g e v a l u e s o f 2

176

CAP ITULO 8. ITERADORES AVANZADOS

1. Lnea 1: La sentencia assert va seguida de cualquier expresin vlida en Python. o a En este caso, la expresion es 1 + 1 == 2 que se evala a True, por lo que la u sentencia assert no hace nada. 2. L nea 2: Sin embargo, si la expresin evala a False, la sentencia assert eleva o u una excepcin AssertionError. o 3. Lnea 6: Puedes incluir un mensaje legible por una persona, que se imprime si se eleva la excepcin AssertionError. o Por ello, esta l nea de cdigo: o
1 a s s e r t l e n ( u n i q u e c h a r a c t e r s ) <= 1 0 , Demasiadas l e t r a s

...es equivalente a sta: e


1 2 i f len ( unique characters ) > 10: r a i s e A s s e r t i o n E r r o r ( Demasiadas l e t r a s )

El solucionador de rompecabezas alfamticos utiliza esta sentencia assert para e terminar en caso de que el rompecabezas contenga ms de diez letras diferentes. a Como a cada letra se le asigna un d gito unico y unicamente existen diez d gitos, un rompecabezas con ms de diez letras diferentes no tendr solucin posible. a a o

8.5.

Expresiones generadoras

Una expresin generadora es como una funcin generadora, pero sin escribir o o la funcin. o
1 2 3 4 5 6 7 8 9 10 >>> u n i q u e c h a r a c t e r s = { E , D , M , O , N , S , R , Y } >>> gen = ( ord ( c ) f o r c in u n i q u e c h a r a c t e r s ) >>> gen <g e n e r a t o r o b j e c t <genexpr> a t 0x00BADC10> >>> next ( gen ) 69 >>> next ( gen ) 68 >>> t u p l e ( ord ( c ) f o r c in u n i q u e c h a r a c t e r s ) (69 , 68 , 77 , 79 , 78 , 83 , 82 , 89)

1. Lnea 2: Una expresin generadora es como una funcin annima que va de o o o volviendo valores. La expresin en s misma se parece bastante a una lista por o compresin, pero se envuelve entre parntesis en lugar de corchetes. o e

8.6. CALCULO DE PERMUTACIONES... DE FORMA PEREZOSA! 2. Lnea 3: La expresin generadora devuelve... un iterador. o

177

3. Linea 5: Al llamar a la funcin next(gen) se devuelve el siguiente valor del o iterador. 4. Lnea 9: Si quieres, puedes iterar a travs de todos los valores convertirlo e en una tupla, lista o conjunto, simplemente pasando la expresin generadora o como parmetro de las funciones constructoras tupla(), list() o set(). En estos a casos no necesitas utilizar el conjunto extra de parntesis simplemente pasa e o la expresin desnuda ord(c) for c unique characters a la funcin tuple() y o Python es capaz de saber que se trata de una expresin generadora. o El uso de una expresin generadora en lugar de una lista por compreo sin puede ahorrar procesador y memoria RAM. Si necesitas construir o una lista unicamente para luego tirarla (por ejemplo, para pasarla como parmetro a una funcin tuple() o set()), utiliza en su lugar una expresin a o o generadora. Esta es otra forma de hacer lo mismo pero utilizando una funcin generadora. o
1 2 3 4 5 def ord map ( a s t r i n g ) : f o r c in a s t r i n g : y i e l d ord ( c ) gen = ord map ( u n i q u e c h a r a c t e r s )

La expresin generadora es ms compacta pero funcionalmente equivalente. o a

8.6.

Clculo de permutaciones... De forma perea zosa!

Ante todo qu diablos son las permutaciones? Se trata de un concepto mae temtico. En realidad existen diferentes deniciones dependiendo del tipo de maa temticas que est haciendo. Aqu estamos hablando de combinatoria, pero si no a e tiene sentido para ti, no te preocupes. Como siempre la wikipedia es tu amiga: http://es.wikipedia.org/wiki/Permutacin o La idea es que tomes una lista de cosas (podr ser nmeros, letras o osos baian u larines) y encuentres todas las formas posibles de dividirlos en listas ms pequeas. a n Todas las listas ms pequeas tendrn el mismo tamao, que puede ser desde 1 a n a n al nmero total de elementos. Ah! y no se pueden repetir. Los matemticos dicen u a

178

CAP ITULO 8. ITERADORES AVANZADOS

vamos a encontrar las permutaciones de tres elementos tomados de dos en dos, lo que signica que la secuencia original tiene tres elementos y quieren encontrar todas las posibles listas ordenadas formadas por dos de ellos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 >>> import i t e r t o o l s >>> perms = i t e r t o o l s . p e r m u t a t i o n s ( [ 1 , 2 , 3 ] , 2 ) >>> next ( perms ) (1 , 2) >>> next ( perms ) (1 , 3) >>> next ( perms ) (2 , 1) >>> next ( perms ) (2 , 3) >>> next ( perms ) (3 , 1) >>> next ( perms ) (3 , 2) >>> next ( perms ) Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> StopIteration

1. Lnea 1: El mdulo itertools contiene toda clase de funciones utiles, incluida o una denominada permutations() que hace el trabajo duro de encontrar permutaciones. 2. Lnea 2: La funcin permutations() toma como parmetros una secuencia (en o a este caso una lista de tres enteros) y un nmero, que es el nmero de elementos u u que debe contener cada grupo ms pequeo. La funcin retorna un iterador, a n o que puedes utilizar en un bucle for o en cualquier sitio en el que haya que iterar. En este ejemplo vamos a ir paso a paso con el iterador de forma manual para mostrar todos los valores. 3. Lnea 3: La primera permutacin de [1, 2, 3] tomando dos elementos cada vez o es (1, 2). 4. Lnea 8: Observa que las permutaciones que se van obteniendo son ordenadas: (2, 1) es diferente a (1, 2). 5. Lnea 15: Eso es! ya estn todas las permutaciones de [1, 2, 3] tomadas de a dos en dos. Las parejas (1, 1) y (2, 2) no se muestran nunca porque contienen repeticiones del mismo elemento por lo que no son permutaciones. Cuando un existen ms permutaciones el iterador eleva una excepcin StopIteration. a o

8.6. CALCULO DE PERMUTACIONES... DE FORMA PEREZOSA! La funcin permutations() no necesita too mar una lista, puede tomar como parmetro a cualquier secuencia incluso una cadena de caracteres.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

179

El mdulo itertools contiene o muchas utilidades

>>> import i t e r t o o l s >>> perms = i t e r t o o l s . p e r m u t a t i o n s ( ABC , 3 ) >>> next ( perms ) ( A , B , C ) >>> next ( perms ) ( A , C , B ) >>> next ( perms ) ( B , A , C ) >>> next ( perms ) ( B , C , A ) >>> next ( perms ) ( C , A , B ) >>> next ( perms ) ( C , B , A ) >>> next ( perms ) Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> StopIteration >>> l i s t ( i t e r t o o l s . p e r m u t a t i o n s ( ABC , 3 ) ) [ ( A , B , C ) , ( A , C , B ) , ( B , A , C ) , ( B , C , A ) , ( C , A , B ) , ( C , B , A ) ]

1. Lnea 2: Una cadena de caracteres es una secuencia de caracteres. Para los propsitos de obtener permutaciones, la cadena ABC es equivalente a la lista o [A, B, C]. 2. Lnea 4: La primera permutacin de los tres elementos [A, B, C] tomados o de tres en tres, es (A, B, C). Hay otras cinco permutaciones los mismos tres caracteres en cualquier orden posible. 3. Lnea 19: Puesto que la funcin permutations() retorna siempre un iterador, o una forma fcil de depurar las permutaciones es pasar ese iterador a la funcin a o interna list() para ver de forma inmediata todas las permutaciones posibles.
1 >>> import i t e r t o o l s 2 >>> l i s t ( i t e r t o o l s . p r o d u c t ( ABC , 123 ) ) 3 [ ( A , 1 ) , ( A , 2 ) , ( A , 3 ) , 4 ( B , 1 ) , ( B , 2 ) , ( B , 3 ) , 5 ( C , 1 ) , ( C , 2 ) , ( C , 3 ) ] 6 >>> l i s t ( i t e r t o o l s . c o m b i n a t i o n s ( ABC , 2 ) ) 7 [ ( A , B ) , ( A , C ) , ( B , C ) ]

180

CAP ITULO 8. ITERADORES AVANZADOS

1. Lnea 2: La funcin itertools.product() devuelve un iterador que contiene el o producto cartesiano de dos secuencias. 2. Lnea 6: La funcin itertools.combinations() devuelve un iterador con todas o las posibles combinaciones de una longitud determinada. Es como la funcin o itertools.permutation(), con la diferencia que las combinaciones no contienen los elementos repetidos en los que la unica diferencia es el orden. Por eso itertools.permutations(ABC, 2) retorna (A, B) y (B, A) (entre otros), pero itertools.combinations(ABC, 2) no retornar (B, A) al ser un uplicado de a (A, B) en orden diferente.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 >>> names = l i s t ( open ( examples / f a v o r i t e p e o p l e . t x t , e n c o d i n g= u t f 8 ) ) >>> names [ Dora\n , Ethan \n , Wesley \n , John \n , Anne\n , Mike\n , C h r i s \n , Sarah \n , Alex \n , L i z z i e \n ] >>> names = [ name . r s t r i p ( ) f o r name in names ] >>> names [ Dora , Ethan , Wesley , John , Anne , Mike , C h r i s , Sarah , Alex , L i z z i e ] >>> names = s o r t e d ( names ) >>> names [ Alex , Anne , C h r i s , Dora , Ethan , John , L i z z i e , Mike , Sarah , Wesley ] >>> names = s o r t e d ( names , key=l e n ) >>> names [ Alex , Anne , Dora , John , Mike , C h r i s , Ethan , Sarah , L i z z i e , Wesley ]

1. Lnea 1: Esta forma de leer un chero retorna una lista formada por todas las l neas del chero de texto. 2. Lnea 5: Desafortunadamente (para este ejemplo), tambin incluye los retornos e de carro al nal de cada l nea. Esta lista por compresin utiliza el mtodo de o e cadenas rstrip() para eliminar los espacios en blanco del nal de cada l nea (Las cadenas de texto tambin tienen una funcin lstrip() para eliminar los e o espacios del comienzo de la cadena y un mtodo strip() para eliminarlos por e ambos lados). 3. Lnea 9: La funcin sorted() toma una lista y la retorna ordenada. Por defecto, o la ordena alfabticamente. e 4. Lnea 13: Pero la funcin sorted() puede tomar un parmetro ms denominado o a a key que se utiliza para ordenar con l. En este caso, la funcin que se utiliza e o es len() por lo que ordena mediante len(cada elemento), por lo que los nombres ms cortos van al principio, seguidos de los ms largos. a a

8.6. CALCULO DE PERMUTACIONES... DE FORMA PEREZOSA!

181

Qu tiene esto que ver con el mdulo itertools? Me alegro de que me hagas e o esa pregunta.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ( . . . como c o n t i n u a c i n de l a c o n s o l a i n t e r a c t i v a a n t e r i o r . . . ) o >>> import i t e r t o o l s >>> g r o u p s = i t e r t o o l s . groupby ( names , l e n ) >>> g r o u p s < i t e r t o o l s . groupby o b j e c t a t 0x00BB20C0> >>> l i s t ( g r o u p s ) [ ( 4 , < i t e r t o o l s . g r o u p e r o b j e c t a t 0x00BA8BF0>) , ( 5 , < i t e r t o o l s . g r o u p e r o b j e c t a t 0x00BB4050 >) , ( 6 , < i t e r t o o l s . g r o u p e r o b j e c t a t 0x00BB4030 >)] >>> g r o u p s = i t e r t o o l s . groupby ( names , l e n ) >>> f o r name length , n a m e i t e r in g r o u p s : ... print ( Nombres con { 0 : d} l e t r a s : . format ( name length ) ) ... f o r name in n a m e i t e r : ... print ( name ) ... Nombres con 4 l e t r a s : Alex Anne Dora John Mike Nombres con 5 l e t r a s : Chris Ethan Sarah Nombres con 6 l e t r a s : Lizzie Wesley

1. Lnea 3: La funcin itertools.groupby() toma una secuencia y una funcin clave o o para retornar un iterador que genera parejas. Cada una de ellas contiene el resultado de la funcin que se usa como clave (key(cada elemento)) y otro o iterador que contiene a todos los elementos que comparten el mismo resultado de la funcin clave. o 2. Lnea 10: Al haber utilizado la funcin list() en la l o nea anterior el iterador se consumi, puesto que ya se ha recorrido cada elemento del iterador para conso truir la lista. No hay un botn reset en un iterador, no puedes volver a usarlo o una vez se ha gastado. Si quieres volver a iterar por l (como en el bucle for e que viene a continuacin), necesitas llamar de nuevo a itertools.groupby() para o crear un nuevo iterador. 3. Lnea 11: En este ejemplo, dada una lista de nombres ya ordenada por longitud, la funcin itertools.groupby(names, len) pondr todos los nombres de cuatro o a

182

CAP ITULO 8. ITERADORES AVANZADOS letras en un iterador, los de cinco letras en otro, y as sucesivamente. La funcin o groupby() es completamente genrica; poder agrupar cadenas por la primera e a letra, los nmeros por el nmero de factores o cualuqier otra funcin clave que u u o puedas imaginar. La funcin itertools.groupby() solamente funciona si la secuencia de eno trada ya est ordenada por la funcin de ordenacin. En el ejemplo ana o o terior, agrupamos una lista de nombres mediante la funcin len(). Esto o funcion bien porque la lista de entrada ya estaba ordenada por longitud. o Ests atento? a

1 2 3 4 5 6 7 8 9 10 11 12

>>> l i s t ( r a n g e ( 0 , 3 ) ) [0 , 1 , 2] >>> l i s t ( r a n g e ( 1 0 , 1 3 ) ) [ 1 0 , 11 , 12] >>> l i s t ( i t e r t o o l s . c h a i n ( r a n g e ( 0 , 3 ) , r a n g e ( 1 0 , 1 3 ) ) ) [ 0 , 1 , 2 , 10 , 11 , 12] >>> l i s t ( z i p ( r a n g e ( 0 , 3 ) , r a n g e ( 1 0 , 1 3 ) ) ) [ ( 0 , 10) , (1 , 11) , (2 , 12)] >>> l i s t ( z i p ( r a n g e ( 0 , 3 ) , r a n g e ( 1 0 , 1 4 ) ) ) [ ( 0 , 10) , (1 , 11) , (2 , 12)] >>> l i s t ( i t e r t o o l s . z i p l o n g e s t ( r a n g e ( 0 , 3 ) , r a n g e ( 1 0 , 1 4 ) ) ) [ ( 0 , 1 0 ) , ( 1 , 1 1 ) , ( 2 , 1 2 ) , ( None , 1 3 ) ]

1. Lnea 5: La funcin itertools.chain() toma dos iteradores y retorna un iterador o que contiene todos los elementos del primer iterador seguidos de todos los elementos del segundo iterador (en realidad puede tomar como parmetros a cualquier nmero de iteradores, los encadenar a todos en el orden en el que u a se pasaron a la funcin). o 2. Lnea 7: La funcin zip() hace algo que suele ser extremadamente util: toma o cualquier nmero de secuencias y retorna un iterador que retorna tuplas con u un elemento de cada secuencia (comenzando por el principio), la primera tupla contiene el primer elemento de cada secuencia, la segunda el segundo de cada secuencia, luego el tercero, y as sucesivamente. 3. Lnea 9: La funcin zip() para cuando se acaba la secuencia ms corta. ran o a ge(10,14) tiene cuatro elementos (10, 11, 12 y 13), pero range(0,3) solamente tiene tres, por lo que la funcin zip() retorna un iterador con tres tuplas. o 4. Lnea 11: De otra parte, la funcin itertools.zip longest() itera hasta el nal o de la secuencia ms larga, insertando valores None en los elementos que coa rresponden a las secuencias que, por ser ms cortas, ya se han consumido a totalmente.

8.6. CALCULO DE PERMUTACIONES... DE FORMA PEREZOSA!

183

De acuerdo, todo eso es muy interesante, pero cmo se relaciona con el soluo cionador de rompecabezas alfamticos? As e :
1 2 3 4 5 6 7 8 >>> c h a r a c t e r s = ( S , M , E , D >>> g u e s s = ( 1 , 2 , 0 , 3 , 4 >>> t u p l e ( z i p ( c h a r a c t e r s , g u e s s ) ) ( ( S , 1 ) , ( M , 2 ) , ( E , 0 ) , ( O , 4 ) , ( N , 5 ) , ( R , 6 ) , >>> d i c t ( z i p ( c h a r a c t e r s , g u e s s ) ) { E : 0 , D : 3 , M : 2 , O : N : 5 , S : 1 , R : 6 , Y : , O , N , R , Y ) , 5 , 6 , 7 ) ( D , 3 ) , ( Y , 7 ) ) 4 , 7 }

1. Lnea 3: Dada una lista de letras y una lista de d gitos (cada uno representado aqu por una cadena de un carcter de longitud), la funcin zip() crear parejas a o a de letras y d gitos en orden. 2. Lnea 6: Porqu es tan util? Porque esta estructura de datos es exactamente la e estructura adecuada para pasarla a la funcin dict() con el n de crear un dico cionario que utilice letras como claves y los d gitos asociados como valores (No es la unica forma de hacerlo, por supuesto. Podr utilizar un diccionario por as compresin para crear el diccionario directamente). Aunque la representacin o o impresa del diccionario muestra las parejas en orden diferente (los diccionarios no tiene orden por s mismos), puedes observar que cada letra est asociada a con el d gito en base a la ordenacin original de las secuencias characters y o guess. El solucionador de rompecabezas alfamticos utiliza esta tcnica para crear un e e diccionario que empareja las letras del rompecabezas con los d gitos de la solucin, o para cada posible solucin. o
1 2 3 4 5 6 c h a r a c t e r s = t u p l e ( ord ( c ) f o r c in s o r t e d c h a r a c t e r s ) d i g i t s = t u p l e ( ord ( c ) f o r c in 0123456789 ) ... f o r g u e s s in i t e r t o o l s . p e r m u t a t i o n s ( d i g i t s , l e n ( c h a r a c t e r s ) ) : ... equation = puzzle . t r a n s l a t e ( dict ( zip ( characters , guess ) ) )

Pero qu hace el mtodo translate() l e e nea 6? Ahora es cuando estamos llegando a la parte realmente divertida.

184

CAP ITULO 8. ITERADORES AVANZADOS

8.7.

Una nueva forma de manipular listas

Las cadenas de Python tienen muchos mtodos. Algunos de ellos los aprendiste e en el cap tulo dedicado a las cadenas: lower(), count() y format(). Ahora quiero explicarte una tcnica potente pero poco conocida de manipulacin de listas: el e o mtodo translate(). e
1 >>> t r a n s l a t i o n t a b l e = { ord ( A ) : ord ( O ) } 2 >>> t r a n s l a t i o n t a b l e 3 { 6 5 : 79} 4 >>> MARK . t r a n s l a t e ( t r a n s l a t i o n t a b l e ) 5 MORK

1. Lnea 1: La traduccin de cadenas comienza con una tabla de traduccin, o o que es un diccionario que empareja un carcter con otro. En realidad, decir a carcter es incorrecto la tabla de traduccin realmente empareja un byte a o con otro. 2. Lnea 2: Recuerda que en Python 3 los bytes son enteros. La funcin ord() o devuelve el valor ASCII de un carcter, lo que, en el caso de A-Z, siempre es a un byte entre el 65 y el 90. 3. Lnea 4: El mtodo translate() se aplica sobre una cadena de texto utilizando e una tabla de traduccin. Reemplaza todas las ocurrencias de las claves de la o tabla de traduccin por los valores correspondientes. En este caso se traduce o MARK a MORK. Qu tiene esto que ver con la resolucin de rompecabezas alfamticos?. Como e o e vers a continuacin: todo. a o
1 2 3 4 5 6 7 8 9 10 11 >>> c h a r a c t e r s = t u p l e ( ord ( c ) f o r c in SMEDONRY ) >>> c h a r a c t e r s (83 , 77 , 69 , 68 , 79 , 78 , 82 , 89) >>> g u e s s = t u p l e ( ord ( c ) f o r c in 91570682 ) >>> g u e s s (57 , 49 , 53 , 55 , 48 , 54 , 56 , 50) >>> t r a n s l a t i o n t a b l e = d i c t ( z i p ( c h a r a c t e r s , g u e s s ) ) >>> t r a n s l a t i o n t a b l e { 6 8 : 5 5 , 6 9 : 5 3 , 7 7 : 4 9 , 7 8 : 5 4 , 7 9 : 4 8 , 8 2 : 5 6 , 8 3 : 5 7 , 8 9 : 50} >>> SEND + MORE == MONEY . t r a n s l a t e ( t r a n s l a t i o n t a b l e ) 9567 + 1085 == 10652

1. Lnea 1: Mediante una expresin generadora calculamos rpidamente los va o a lores de los bytes de cada carcter de una cadena. characters es un ejemplo del a valor que puede contener sorted characters en la funcin alphametics.solve(). o

8.8. EVALUACION DE CADENAS DE TEXTO COMO EXPRESIONES DE PYTHON185 2. Lnea 4: Mediante el uso de otra expresin generadora calculamos rpidamente o a los valores de los bytes de cada d gito de esta cadena. El resultado, guess, se encuentra en la forma que retorna la funcin itertools.permutations() en la o funcin alphametics.solve(). o 3. Lnea 7: Esta tabla de traduccin se genera mediante la funcin zip() uniendo o o los characters y guess en un diccionario. Esto es exactamente lo que la funcin o alphametics.solve() hace en el bucle for. 4. Lnea 10: Finalmente, pasamos esta tabla de traduccin al mtodo translate() o e aplicndolo a la cadena original del rompecabezas. Esto convierte cada letra a de la cadena en el d gito que le corresponde (basado en las letras existentes en characters y los d gitos de guess). El resultado es una expresin vlida de o a Python, aunque en forma de cadena de texto. Esto es impresionante. Pero Qu puedes hacer con una cadena que representa e a una expresin vlida de Python? o a

8.8.

Evaluacin de cadenas de texto como expreo siones de Python

Esta es la pieza nal del rompecabezas (o mejor dicho, la pieza nal del solucionador de rompecabezas). Despus de tanta manipulacin de cadenas tan moderna, e o nos ha quedado una cadena como 9567 + 1085 == 10652. Pero es una cadena de texto, y para qu nos vale una cadena de texto? Pide paso eval(), la herramienta e universal para evaluacin de expresiones en Python. o
1 >>> e v a l ( 1 + 1 == 2 ) 2 True 3 >>> e v a l ( 1 + 1 == 3 ) 4 False 5 >>> e v a l ( 9567 + 1085 == 10652 ) 6 True

Pero espera, hay ms! La funcin eval() no se limita a expresiones booleanas. a o Puede manejar cualquier expresin en Python y devolver cualquier tipo de datos. o

186
1 2 3 4 5 6 7 8 >>> e v a l ( AB >>> e v a l ( MORK >>> e v a l ( 5 >>> e v a l ( [ , A + B )

CAP ITULO 8. ITERADORES AVANZADOS

MARK . t r a n s l a t e ( { 6 5 : 7 9 } ) ) AAAAA . count ( A ) ) [] 5 ) , , , ]

Pero espera, que eso no es todo!


1 2 3 4 5 6 7 8 >>> x = 5 >>> e v a l ( x 5 ) 25 >>> e v a l ( pow ( x , 2 ) ) 25 >>> import math >>> e v a l ( math . s q r t ( x ) ) 2.2360679774997898

1. Lnea 2: La expresin que eval() recibe como parmetro puede referenciar a o a cualquier variable global denida fuera de la funcin eval(). Si se llama desde o una funcin, tambin puede referenciar variables locales. o e 2. Lnea 4: Y funciones. 3. Lnea 7: Y mdulos. o Pero espera un minuto...
1 >>> import s u b p r o c e s s 2 >>> e v a l ( s u b p r o c e s s . g e t o u t p u t ( l s ) ) 3 Desktop Library Pictures \ 4 Documents Movies Public \ 5 Music Sites 6 >>> e v a l ( s u b p r o c e s s . g e t o u t p u t ( rm /some/random/ f i l e ) )

1. Lnea 2: El mdulo subprocess te permite ejecutar comandos de la l o nea de comandos y obtener el resultado como una cadena de Python. 2. Lnea 6: Los comandos de la l nea de comandos pueden producir resultados permanentes. Es incluso peor que esto, porque existe una funcin global import () que o toma como parmetro el nombre de un mdulo como cadena de texto, importa el a o

8.8. EVALUACION DE CADENAS DE TEXTO COMO EXPRESIONES DE PYTHON187 mdulo y devuelve una referencia a l. Combinado con el poder de la funcin eval() o e o puedes construir una expresin que te borre todos los cheros: o
1 >>> e v a l ( import ( s u b p r o c e s s ) . g e t o u t p u t ( rm /some/random/ f i l e ) )

Ahora imagina la salida de rm -rf . Realmente no habr ninguna salida por a pantalla, pero tampoco te habr quedado ningn chero en tu cuenta de usuario. a u

eval() es MALIGNO
Bueno, lo maligno de de eval es la posibilidad de evaluar expresiones procedentes de fuentes que no sean de conanza. Solamente deber utilizar eval() para as entradas de conanza. Lo complicado es saber qu es de conanza. Pero hay e algo que debes tener seguro: no deber tomar este solucionador de rompecabezas as alfamticos y ponerlo en Internet como un servicio web. No cometas el error de pene sar, la funcin hace un montn de manipulaciones de cadenas antes de evaluar la o o cadena; no puedo imaginar cmo alguien podr explotar eso. Alguien descubrir una o a a forma de introducir algn cdigo maligno que traspase toda la manipulacin de cau o o denas previa(echa un vistazo a: http://www.securityfocus.com/blogs/746 y vers a que cosas ms raras han pasado), y podrs irte despidiendo de tu servidor. a a Pero... Seguro que existe una forma de evaluar expresiones de forma segura? Para que eval() se ejecute en un entorno aislado en el que no se pueda acceder a l e desde el exterior? S y no.
1 2 3 4 5 6 7 8 9 10 11 12 13 >>> x = 5 >>> e v a l ( x 5 , { } , { } ) Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> F i l e <s t r i n g > , l i n e 1 , in <module> NameError : name x i s not d e f i n e d >>> e v a l ( x 5 , { x : x } , { } ) >>> import math >>> e v a l ( math . s q r t ( x ) , {x : x } , { } ) Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> F i l e <s t r i n g > , l i n e 1 , in <module> NameError : name math i s not d e f i n e d

1. Lnea 2: El segundo y tercer parmetro que se pasen a la funcin eval() actan a o u como los espacios de nombre local y global para evaluar la expresin. En este o caso ambos estn vac lo que signica que cuando se evala la cadena x * a os, u 5 no existe referencia a x ni en el espacio de nombres local ni en el global, por lo que eval() genera una excepcin. o

188

CAP ITULO 8. ITERADORES AVANZADOS

2. Lnea 7: Puedes incluir selectivamente valores espec cos en el espacio de nombres global listndolos individualmente. Entonces, sern las unicas variables a a que estarn disponibles durante la evaluacin. a o 3. Lnea 9: Incluso aunque hayas importado el mdulo math, no se incluy en el o o espacio de nombres que se pas a la funcin eval(), por lo que la evaluacin o o o falla. Parece que fue fcil. Djame ya que haga un servicio web con el solucionador a e de rompecabezas alfamtico! e
1 >>> e v a l ( pow ( 5 , 2 ) , { } , { } ) 2 25 3 >>> e v a l ( i m p o r t ( math ) . s q r t ( 5 ) , { } , { } ) 4 2.2360679774997898

1. Lnea 1: Incluso aunque hayas pasado diccionarios vac para el espacio de os nombres local y global, siguien estando disponibles todas las funciones internas de Python. Por eso pow(5, 2) funciona, porque 5 y 2 son literales y pow() es una funcin interna. o 2. Lnea 3: Desafortunadamente (y si no ves porqu es desafortunado, sigue le e e yendo), la funcin import () es interna, por lo que tambin funciona. o Vale, eso signica que an puedes hacer cosas malignas, incluso aunque exu plicitamente establezcas los espacios de nombre local y global a diccionarios vac os, cuando llamas a la funcin eval(). o
1 >>> e v a l ( import ( s u b p r o c e s s ) . g e t o u t p u t ( rm /some/random/ f i l e ) , { } , { } )

Vale, estoy contento de no haber hecho el servicio web del solucionador de rompecabezas alfamtico. Hay algn modo de usar eval() de forma segura? Bueno, e u s y no.
1 >>> e v a l ( i m p o r t ( math ) . s q r t ( 5 ) , 2 ... { b u i l t i n s : None } , { } ) 3 Traceback ( most r e c e n t c a l l l a s t ) : 4 F i l e <s t d i n > , l i n e 1 , in <module> 5 F i l e <s t r i n g > , l i n e 1 , in <module> 6 NameError : name i m p o r t i s not d e f i n e d 7 >>> e v a l ( i m p o r t ( s u b p r o c e s s ) . g e t o u t p u t ( rm 8 ... { b u i l t i n s : None } , { } ) 9 Traceback ( most r e c e n t c a l l l a s t ) : 10 F i l e <s t d i n > , l i n e 1 , in <module> 11 F i l e <s t r i n g > , l i n e 1 , in <module> 12 NameError : name i m p o r t i s not d e f i n e d

r f

/ ) ,

8.9. JUNTANDOLO TODO

189

1. Lnea 2: Para evaluar de forma segura expresiones en las que no conf ne es, cesitas denir el diccionario del espacio de nombres global para que mapee builtins a None, el valor nulo de Python. Internamente, las funciones internas se encuentran en un pseudo-mdulo denominado builtins . Este o pseudo-mdulo (el conjunto de funciones internas) est disponible para evao a luar expresiones a menos que expresamente lo sustituyas. 2. Lnea 8: Asegrate de que sustituyes builtins . No builtin , built-ins u o alguna otra variacin parecida que te exponga a riesgos catastrcos. o o As que ahora ya es seguro utilizar eval()? Bueno, s y no.
1 >>> e v a l ( 2 2147483647 , 2 ... { b u i l t i n s : None } , { } )

Incluso sin acceso a las funciones internas, puedes lanzar an un ataque de u denegacin del servicio. Por ejemplo, elevar 2 a la potencia 2147483647 har que el o a uso de la CPU del servidor se eleve al 100 % durante algn tiempo (si pruebas esto u en modo interactivo pulsa Ctrl-C varias veces hasta que se cancele). Tcnicamente e esta expresin retornar un valor en algn momento, pero mientras tanto el servidor o a u estar ocupado. a Al nal, es posible evaluar de forma segura expresiones Python de fuentes que no sean de conanza, para una denicin de seguro que no es muy util en la vida o real. Est bien que hagas pruebas, y est bien si solamente le pasas datos de fuentes a a de conanza. Pero cualquier otra cosa es est buscando problemas. a

8.9.

Juntndolo todo a

Para recapitular: este programa resuelve rompecabezas alfamticos mediante la e fuerza bruta, a travs de la bsqueda exhaustiva de todas las posibles combinaciones e u de solucin. Para hacer esto, los pasos que sigue son: o 1. Encuentra todas las letras del rompecabezas con la funcin re.ndall(). o 2. Determina las letras que son, sin repeticin, utilizando conjuntos y la funcin o o set(). 3. Comprueba si hay ms de 10 letras diferentes (lo que signicar que el roma a pecabezas no tiene solucin) con la sentencia assert. o 4. Convierte las letras a sus equivalentes en ASCII con un objeto generador.

190

CAP ITULO 8. ITERADORES AVANZADOS

5. Calcula todas las posibles soluciones con la funcin itertools.permutations(). o 6. Convierte cada una de las soluciones posibles a una expresin en Python con o el mtodo de cadenas de texto translate(). e 7. Prueba cada una de las posibles soluciones evaluando la expresin Python con o la funcin eval(). o 8. Devuelve la primera solucin que se evala a True. o u ...en slo catorce l o neas de cdigo. o

8.10.

Lecturas recomendadas

el mdulo itertools: o http://docs.python.org/3.1/library/itertools.html itertoolsFunciones iteradoras para bucles ecientes: http://www.doughellmann.com/PyMOTW/itertools/ Video de Raymond Hettinger con la charla Inteligencia Articial sencilla con Python en la PyCon 2009: http://blip.tv/le/1947373/ Receta 576615 - solucionador de rompecabezas alfamticos de Raymond Hete tinger para Python 2: http://code.activestate.com/recipes/576615/ Ms recetas de Raymond Kettinger en el repositorio de cdigo ActiveState: a o http://code.activestate.com/recipes/users/178123/ Alfamtica en la wikipedia: e http://en.wikipedia.org/wiki/Verbal arithmetic Indice alfamtico: e http://www.tkcs-collins.com/truman/alphamet/index.shtml Con muchos rompecabezas: http://www.tkcs-collins.com/truman/alphamet/alphamet.shtml Y un generador de rompecabezas alfamticos: e http://www.tkcs-collins.com/truman/alphamet/alpha gen.shtml Muchas gracias a Raymond Hattinger por permitirme relicenciar su cdigo o para que pudiera portarlo a Python 3 y utilizarlo como base para este cap tulo.

Cap tulo 9 Pruebas unitarias


Nivel de dicultad:    La certidumbre no es prueba de que algo sea cierto. Hemos estado tan seguros de tantas cosas que luego no lo eran. Oliver Wendell Holmes, Jr

9.1.

(Sin) Inmersin o

En este cap tulo vas a escribir y depurar un conjunto de funciones de utilidad para convertir nmeros romanos (en ambos sentidos). En el caso de estudio de los u nmeros romanos ya vimos cual es la mecnica para construir y validar nmeros u a u romanos. Ahora vamos a volver a l para considerar lo que nos llevar expandirlo e a para que funcione como una utilidad en ambos sentidos. Las reglas para los nmeros romanos sugieren una serie de interesantes obseru vaciones: Solamente existe una forma correcta de representar un nmero cualquiera con u los d gitos romanos. Lo contrario tambin es verdad: si una cadena de caracteres es un nmero e u romano vlido, representa a un unico nmero (solamente se puede interpretar a u de una forma). Existe un rango limitado de valores que se puedan expresar como nmeros u romanos, en concreto del 1 al 3999. Los romanos ten varias maneras de an expresar nmeros mayores, por ejemplo poniendo una barra sobre un nmero u u 191

192

CAP ITULO 9. PRUEBAS UNITARIAS para indicar que el valor normal que representaba ten que multiplicarse por a 1000. Para los propsitos que perseguimos en este cap o tulo, vamos a suponer que los nmeros romanos van del 1 al 3999. u No existe ninguna forma de representar el 0 en nmeros romanos. u No hay forma de representar nmeros negativos en nmeros romanos. u u No hay forma de representar fracciones o nmeros no enteros con nmeros u u romanos.

Vamos a comenzar explicando lo que el mdulo roman.py deber hacer. Tendr que o a a tener dos funciones principales to roman() y from romman(). La funcin to roman() o deber tomar como parmetro un nmero entero de 1 a 3999 y devolver su reprea a u sentacin en nmeros Romanos como una cadena... o u Paremos aqu Vamos a comenzar haciendo algo un poco inesperado: escribir . un caso de prueba que valide si la funcin to roman() hace lo que quieres que haga. o Lo has le bien: vas a escribir cdigo que valide la funcin que an no has escrito. do o o u A esto se le llama desarrollo dirigido por las pruebas test driven development, o TDD. El conjunto formado por las dos funciones de conversin to roman(), o y from roman() se puede escribir y probar como una unidad, separadamente de cualquier programa mayor que lo utilice. Python dispone de un marco de trabajo (framework) para pruebas unitarias, el mdulo se llama, apropiadamente, unittest. o La prueba unitaria es una parte importante de una estrategia de desarrollo centrada en las pruebas. Si escribes pruebas unitarias es importante escribirlas pronto y mantenerlas actualizadas con el resto del cdigo y con los cambios de requisitos. o Muchas personas dicen que las pruebas se escriban antes que el cdigo que se vaya o a probar, y este es el estilo que voy a ensearte en este cap n tulo. De todos modos, las pruebas unitarias son utiles independientemente de cuando las escribas. Antes de escribir el cdigo, el escribir las pruebas unitarias te oblica a detallar o los requisitos de forma util. Durante la escritura del cdigo, las pruebas unitarias evitan que escribas demao siad cdigo. Cuando pasan los todos los casos de prueba, la funcin est como o a pleta. Cuando se est reestructurando el cdigo1 , pueden ayudar a probar que la a o nueva versin se comporta de igual manera que la vieja. o
1

refactoring

9.2. UNA UNICA PREGUNTA

193

Cuando se mantiene el cdigo, disponer de pruebas te ayuda a cubrirte el o trasero cuando alquien viene gritando que tu ultimo cambio en el cdigo ha o roto el antiguo que ya funcionaba. Cuando codicas en un equipo, disponer de un juego de pruebas completo disminuye en gran medida la probabilidad de que tu cdigo rompa el de otra o persona, ya que puedes ejecutar los casos de prueba antes de introducir cambios (He visto esto pasar en las competiciones de cdigo. Un equipo trocea el trabajo o asignado, todo el mundo toma las especicaciones de su tarea, escribe los casos de prueba para ella en primer lugar, luego comparte sus casos de prueba con el resto del equipo. De ese modo, nadie puede perderse demasiado desarrollando cdigo que no funcione bien con el del resto). o

9.2.

Una unica pregunta

Un caso de prueba (unitaria) contesta a una unica pregunta sobre el cdigo o que est probando. Un caso de prueba (unitaria) deber ser capaz de... a a ...ejecutarse completamente por s mismo, sin necesidad de ninguna entrada manual. Las pruebas unitarias deben ser automticas. a ...determinar por s misma si la funcin que se est probando ha funcionado o a correctamente o a fallado, sin la necesidad de que exista una persona que interprete los resultados. ...ejecutarse de forma aislada, separada de cualquier otro caso de prueba (incluso aunque estos otros prueben las mismas funciones). Cada caso de prueba es una isla. Dado esto, construyamos un caso de prueba para el primer requisito: La funcin to roman() deber devolver o a el nmero Romano que represente a los u nmeros enteros del 1 al 3999. u Lo que no es obvio es cmo el cdigo siguiente efecta dicho clculo. Dene o o u a una clase que no tiene mtodo init (). La clase tiene otro mtodo, pero nunca se e e llama. El cdigo tiene un bloque main , pero este bloque ni referencia a la clase o ni a su mtodo. Pero hace algo, te lo juro. e

Toda prueba es una isla.

194

CAP ITULO 9. PRUEBAS UNITARIAS

1 # r o m a n t e s t 1 . py 2 import roman1 3 import u n i t t e s t 4 5 c l a s s KnownValues ( u n i t t e s t . TestCase ) : 6 known values = ( ( 1 , I ) , ( 2 , I I ) , 7 ( 3 , I I I ) , ( 4 , IV ) , 8 ( 5 , V ) , ( 6 , VI ) , 9 ( 7 , VII ) , ( 8 , V I I I ) , 10 ( 9 , IX ) , ( 1 0 , X ) , 11 ( 5 0 , L ) , ( 1 0 0 , C ) , 12 ( 5 0 0 , D ) , ( 1 0 0 0 , M ) , 13 ( 3 1 , XXXI ) , ( 1 4 8 , CXLVIII ) , 14 ( 2 9 4 , CCXCIV ) , ( 3 1 2 , CCCXII ) , 15 ( 4 2 1 , CDXXI ) , ( 5 2 8 , DXXVIII ) , 16 ( 6 2 1 , DCXXI ) , ( 7 8 2 , DCCLXXXII ) , 17 ( 8 7 0 , DCCCLXX ) , ( 9 4 1 , CMXLI ) , 18 ( 1 0 4 3 , MXLIII ) , ( 1 1 1 0 , MCX ) , 19 ( 1 2 2 6 , MCCXXVI ) , ( 1 3 0 1 , MCCCI ) , 20 ( 1 4 8 5 , MCDLXXXV ) , ( 1 5 0 9 , MDIX ) , 21 ( 1 6 0 7 , MDCVII ) , ( 1 7 5 4 , MDCCLIV ) , 22 ( 1 8 3 2 , MDCCCXXXII ) , ( 1 9 9 3 , MCMXCIII ) , 23 ( 2 0 7 4 , MMLXXIV ) , ( 2 1 5 2 , MMCLII ) , 24 ( 2 2 1 2 , MMCCXII ) , ( 2 3 4 3 , MMCCCXLIII ) , 25 ( 2 4 9 9 , MMCDXCIX ) , ( 2 5 7 4 , MMDLXXIV ) , 26 ( 2 6 4 6 , MMDCXLVI ) , ( 2 7 2 3 , MMDCCXXIII ) , 27 ( 2 8 9 2 , MMDCCCXCII ) , ( 2 9 7 5 , MMCMLXXV ) , 28 ( 3 0 5 1 , MMMLI ) , ( 3 1 8 5 , MMMCLXXXV ) , 29 ( 3 2 5 0 , MMMCCL ) , ( 3 3 1 3 , MMMCCCXIII ) , 30 ( 3 4 0 8 , MMMCDVIII ) , ( 3 5 0 1 , MMMDI ) , 31 ( 3 6 1 0 , MMMDCX ) , ( 3 7 4 3 , MMMDCCXLIII ) , 32 ( 3 8 4 4 , MMMDCCCXLIV ) , ( 3 8 8 8 , MMMDCCCLXXXVIII ) , 33 ( 3 9 4 0 , MMMCMXL ) , ( 3 9 9 9 , MMMCMXCIX ) ) 34 35 def t e s t t o r o m a n k n o w n v a l u e s ( s e l f ) : 36 to roman s h o u l d g i v e known r e s u l t with known i n p u t 37 f o r i n t e g e r , numeral in s e l f . known values : 38 r e s u l t = roman1 . to roman ( i n t e g e r ) 39 s e l f . a s s e r t E q u a l ( numeral , r e s u l t ) 40 41 i f name == m a i n : 42 u n i t t e s t . main ( )

1. Lnea 5: Para escribir un caso de prueba, lo primero es crear una subclase de TestCase del mdulo unittest. Esta clase proporciona muchos mtodos utiles o e que se puedne utilizar en los casos de prueba para comprobar condiciones espec cas.

9.2. UNA UNICA PREGUNTA

195

2. Lnea 33: Esto es una lista de pares de nmeros enteros y sus nmeros romanos u u equivalentes que he vericado manualmente. Incluye a los diez primeros nmeu ros, el mayor, todos los nmeros que se convierten un unico carcter romano, y u a una muestra aleatoria de otros nmeros vlidos. No es necesario probar todas u a las entradas posibles, pero deber probarse los casos de prueba l an mite. 3. Lnea 35: Cada caso de prueba debe escribirse en su propio mtodo, que no e debe tomar parmetros y no debe devolver ningn valor. Si el mtodo nalia u e za normalmente sin elevar ninguna excepcin, se considera que la prueba ha o pasado; si el mtodo eleva una excepcin, se considera que la prueba ha fallado. e o 4. Lnea 38: Este es el punto en el que se llama a la funcin to roman() (bueno, o la funcin an no se ha escrito, pero cuando est escrita, esta ser la l o u e a nea que la llamar). Observa que con esto has denido la API de la funcin to roman(): a o debe tomar como parmetro un nmero entero (el nmero a convertir) y devola u u ver una cadena (la representacin del nmero entero en nmeros romanos). Si o u u la API fuese diferente a esta, este test fallar. Observa tambin que no estamos a e capturando excepciones cuando llamamos a la funcin to roman(). Es inteno a o cionado, to roman() no deber devolver una excepcin cuando la llames con una entrada vlida, y todas las entradas previstas en este caso de prueba son a o a vlidas. Si to roman() elevase una excepcin, esta prueba deber considerarse a fallida. 5. Lnea 38: Asumiendo que la funcin to roman() fuese denida correctamen o te, llamada correctamente, ejecutada correctamente y retornase un valor, el ultimo paso es validar si el valor retornado es el correcto. Esta es una pre gunta habitual, y la clase TestCase proporciona un mtodo, assertEqual, para e validar si dos valores son iguales. Si el resultado que retorna to roman() (result) no coincide con el valor que se estaba esperando (numeral), assertEqual elevar una excepcin y la prueba fallar. Si los dos valores son iguales, asa o a sertEqual no har nada. Si todos los valores que retorna to roman() coinciden a con los valores esperados, la funcin assertEqual nunca eleva una excepcin, o o por lo que la funcin test to roman known values termina normalmente, lo que o signica que la funcin to roman() ha pasado esta prueba. o Cuando ya tienes un caso de prueba, puedes comenzar a codicar la funcin to roman(). o Primero deber crearla como una funcin vac as o a y asegurarte de que la prueba falla. Si la prueba funciona antes de que hayas escrito ningn cdiu o go, tus pruebas no estn probando nada!! La a

Escribe un caso de prueba que falle, luego codica hasta que funcione.

196

CAP ITULO 9. PRUEBAS UNITARIAS

prueba unitaria es como un baile: la prueba va llevando, el cdigo la va siguiendo. o Escribe una prueba que falle, luego codica hasta que el cdigo pase la prueba. o
1 # roman1 . py 2 3 def to roman ( n ) : 4 c o n v e r t i n t e g e r t o Roman numeral 5 pass

1. Lnea 5: En este momento debes denir la API de la funcin to roman(), pero o no quieres codicarla an (Las pruebas deben fallar primero). Para conseguirlo, u utiliza la palabra reservada de Python pass que, precisamente, sirve para no hacer nada. Ejecuta romantest1.py en la l nea de comando para ejecutar la prueba. Si lo llamas con la opcin -v de la l o nea de comando, te mostrar una salida con ms a a informacin de forma que puedas ver lo que est sucediendo conforme se ejecutan o a los casos de prueba. Con suerte, la salida deber parecerse a esto: a
1 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 r o m a n t e s t 1 . py v 2 t e s t t o r o m a n k n o w n v a l u e s ( m a i n . KnownValues ) 3 to roman s h o u l d g i v e known r e s u l t with known i n p u t . . . FAIL 4 5 ====================================================================== 6 FAIL : to roman s h o u l d g i v e known r e s u l t with known i n p u t 7 8 Traceback ( most r e c e n t c a l l l a s t ) : 9 F i l e r o m a n t e s t 1 . py , l i n e 7 3 , in t e s t t o r o m a n k n o w n v a l u e s 10 s e l f . a s s e r t E q u a l ( numeral , r e s u l t ) 11 A s s e r t i o n E r r o r : I != None 12 13 14 Ran 1 t e s t in 0 . 0 1 6 s 15 16 FAILED ( f a i l u r e s =1)

1. Lnea 2: Al ejecutar el programa se ejecuta unittest.main(), que ejecuta el caso de prueba. Cada caso de prueba es un mtodo de una clase en romantest.py. No e se requiere ninguna organizacin de estas clases de prueba; pueden contener o un mtodo de prueba cada una o puede existir una unica clase con muchos e mtodos de prueba. El unico requisito es que cada clase de prueba debe heredar e de unittest.TestCase. 2. Lnea 3: Para cada caso de prueba, el mdulo unittest imprimir el docstring o a

9.2. UNA UNICA PREGUNTA

197

(cadena de documentacin) del motodo y si la prueba ha pasado o ha fallado. o e En este caso, como se esperaba, la prueba ha fallado. 3. Lnea 11: Para cada caso de prueba que falle, unittest muestra la informacin o de traza que muestra exactamente lo que ha sucedido. En este caso la llamada a assertEqual() elev la excepcin AssertionError porque estaba esperando que o o to roman(1) devolviese I, y no o ha devuelto (Debido a que no hay valor de retorno expl cito, la funcin devolvi None, el valor nulo de Python). o o 4. Lnea 14: Despus del detalle de cada prueba, unittest muestra un resumen de e los test que se han ejecutado y el tiempo que se ha tardado. 5. Lnea 16: En conjunto, la ejecucin de la prueba ha fallado puesto que al o menos un caso de prueba lo ha hecho. Cuando un caso de prueba no pasa, unittest distingue entre fallos y errores. Un fallo se produce cuando se llama a un mtodo assertXYZ, como assertEqual o assertRaises, que fallan porque la e condicin que se evala no sea verdadera o la excepcin que se esperaba no o u o ocurri. Un error es cualquier otra clase de excepcin en el cdigo que ests o o o a probando o en el propio caso de prueba. Ahora, nalmente, puedes escribir la funcin to roman(). o
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 roman numeral map = ( ( ( ( ( ( ( ( ( ( ( ( ( ( M , CM D , CD C , XC L , XL X , IX V , IV I , 1000) , , 900) , 500) , , 400) , 100) , , 90) , 50) , , 40) , 10) , , 9) , 5) , , 4) , 1))

def to roman ( n ) : c o n v e r t i n t e g e r t o Roman numeral result = f o r numeral , i n t e g e r in roman numeral map : while n >= i n t e g e r : r e s u l t += numeral n = i n t e g e r return r e s u l t

198

CAP ITULO 9. PRUEBAS UNITARIAS

1. Lnea 13: La variable roman numeral map es una tupla de tuplas que dene tres cosas: las representaciones de caracteres de los nmeros romanos bsicos; u a el orden de los nmeros romanos (en orden descendente, desde M a I), y el valor u de cada nmero romano. Cada tupla interna es una pareja (nmero romano, u u valor). No solamente dene los nmeros romanos de un unico carcter, tambin u a e dene las parejas de dos caracteres como CM (ien menos que mil). As el c a cdigo de la funcin to roman se hace ms simple. o o 2. Lnea 19: Es aqu en donde la estructura de datos roman numeral map se mues tra muy util, porque no necesitas ninguna lgica especial para controlar la o regla de restado. Para convertir nmeros romanos solamente tienes que iterar u u a travs de la tupla roman numeral map a la bsqueda del mayor valor entero e que sea menor o igual al valor introducido. Una vez encontrado, se concatena su representacin en nmero romano al nal de la salida, se resta el valor o u correspondiente del valor inicial y se repite hasta que se consuman todos los elementos de la tupla. n Si an no ves claro cmo funciona la funcin to roman() aade una llamada a u o o print() al nal del bucle while:
1 2 3 4 5 6 7 8 ... while n >= i n t e g e r : r e s u l t += numeral n = i n t e g e r print ( r e s t a n d o {0} de l a entrada , sumando {1} a l a s a l i d a . format ( i n t e g e r , numeral ) ) ...

Con estas sentencias, la salida es la siguiente:


1 >>> import roman1 2 >>> roman1 . to roman ( 1 4 2 4 ) 3 r e s t a n d o 1000 de l a entrada , sumando M 4 r e s t a n d o 400 de l a entrada , sumando CD 5 r e s t a n d o 10 de l a entrada , sumando X a 6 r e s t a n d o 10 de l a entrada , sumando X a 7 r e s t a n d o 4 de l a entrada , sumando IV a 8 MCDXXIV

a la salida a la salida la salida la salida la salida

Por lo que parece que funciona bien la funcin to roman(), al menos en esta o prueba manual. Pero, Pasar el caso de prueba que escribimos antes? a

9.3. PARA Y PRENDELE FUEGO

199

1 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 r o m a n t e s t 1 . py v 2 t e s t t o r o m a n k n o w n v a l u e s ( m a i n . KnownValues ) 3 to roman s h o u l d g i v e known r e s u l t with known i n p u t . . . ok 4 5 6 Ran 1 t e s t in 0 . 0 1 6 s 7 8 OK

1. Lnea 3: Bien! la funcin to roman() pasa los valores conocidos del caso de o prueba. No es exhaustivo, pero revisa la funcin con una amplia variedad de o entradas. Incluyendo entradas cuyo resultado son todos los nmeros romanos u de un unico carcter, el valor mayor (3999) y la entrada que produce el nmero a u romano ms largo posible (3888). En este punto puedes conar razonablemente a en que la funcin est bien hecha y funciona para cualquier valor de entrada o a vlido que puedas consultar. a Valores vlidos Qu es lo que pasar con valores de entrada no vlidos? a e a a

9.3.

Para y prndele fuego e


La forma de parar la ejecucin o para indicar un fallo es elevar una excepcin o

No es suciente con probar que las funciones pasan las pruebas cuando stas incluyen vae lores correctos; tienes que probar que las funciones fallan cuando se les pasa una entrada no vlida. Y que el fallo no es uno cualquiera; deben a fallar de la forma que esperas.
1 2 3 4 5 6 7 >>> import roman1 >>> roman1 . to roman ( 4 0 0 0 ) M M M M >>> roman1 . to roman ( 5 0 0 0 ) M M M MM >>> roman1 . to roman ( 9 0 0 0 ) M M M M M MMMM

1. Lnea 6: Esto no es lo que quer ni siquiera es un nmero romano vlido. as u a De hecho, todos estos nmeros estn fuera del rango de los que son aceptables u a como entrada a la funcin, pero an as la funcin devuelve valores falsos. Deo u , o volver valores malos sin advertencia alguna es algo maaaalo. Si un programa

200

CAP ITULO 9. PRUEBAS UNITARIAS debe fallar, lo mejor es que lo haga lo ms cerca del error, rpida y evidena a temente. Mejor parar y prenderle fuego como diche el dicho. La forma de hacerlo en Python es elevar una excepcin. o

La pregunta que te tienes que hacer es, Cmo puedo expresar esto como un o requisito que se pueda probar? Qu tal esto para comenzar?: e a o La funcin to roman() deber elevar una excepcin OutOfRangeError o cuando se le pase un nmero entero mayor que 3999. u Cmo ser esta prueba? o a
1 2 3 4 c l a s s ToRomanBadInput ( u n i t t e s t . TestCase ) : def t e s t t o o l a r g e ( s e l f ) : to roman d e b e r f a l l a r con una e n t r a d a muy grande a s e l f . a s s e r t R a i s e s ( roman2 . OutOfRangeError , roman2 . to roman , 4 0 0 0 )

1. Lnea 1: Como en el caso de prueba anterior, has creado una clase que hereda de unittest.TestCase. Puedes crear ms de una prueba por cada clase (como a vers ms adelante en este mismo cap a a tulo), pero aqu he elegido crear una clase nueva porque esta prueba es algo diferente a la anterior. En este ejemplo, mantendremos todas las pruebas sobre entradas vlidas en una misma clase y a todas las pruebas que validen entradas no vlidas en otra clase. a 2. Lnea 2: Como en el ejemplo anterior, la prueba es un mtodo de la clase que e tenga un nombre que comience por test. 3. Lnea 4: La clase unittest.TestCase proporciona el mtodo assertRaises, que e toma los siguientes parmetros: la excepcin que se debe esperar, la funcin a o o que se est probando y los parmetros que hay que pasarle a la funcin (Si a a o la funcin que ests probando toma ms de un parmetro hay que pasarlos o a a a todos assertRaises en el orden que los espera la funcin, para que assertRaises o los pueda pasar, a su vez, a la funcin a probar. o Presta atencin a esta ultima l o nea de cdigo. En lugar de llamar directao o mente a la funcin to roman() y validar a mano si eleva una excepcin concreta o (mediante un bloque try ... except), el mtodo assertRaises se encarga de ello por e nosotros. Todo lo que hay que hacer es decirle la excepcin que estamos esperao do (roman.OutOfRangeError, la funcin (to roman()) y los parmetros de la funcin o a o (4000). El mtodo assertRaises se encarga de llamar a la funcin to roman() y valida e o que eleve roman2.OutRangeError.

9.3. PARA Y PRENDELE FUEGO

201

Observa tambin que ests pasando la propia funcin to roman() como un e a o parmetro; no ests ejecutndola y no ests pasando el nombre de la funcin como a a a a o una cadena. He mencionado ya lo oportuno que es que todo en Python sea un objeto? Qu sucede cuando ejecutas esta suite de pruebas con esta nueva prueba? e
1 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 r o m a n t e s t 2 . py v 2 t e s t t o r o m a n k n o w n v a l u e s ( main . KnownValues ) 3 to roman s h o u l d g i v e known r e s u l t with known i n p u t . . . ok 4 t e s t t o o l a r g e ( main . ToRomanBadInput ) 5 to roman d e b e r f a l l a r con una e n t r a d a muy grande . . . ERROR a 6 7 ====================================================================== 8 ERROR: to roman d e b e r f a l l a r con una e n t r a d a muy grande a 9 10 Traceback ( most r e c e n t c a l l l a s t ) : 11 F i l e r o m a n t e s t 2 . py , l i n e 7 8 , in t e s t t o o l a r g e 12 s e l f . a s s e r t R a i s e s ( roman2 . OutOfRangeError , roman2 . to roman , 4 0 0 0 ) 13 A t t r i b u t e E r r o r : module o b j e c t has no a t t r i b u t e OutOfRangeError 14 15 16 Ran 2 t e s t s in 0 . 0 0 0 s 17 18 FAILED ( e r r o r s =1)

1. Lnea 5: Deber esperar que fallara (puesto que an no has escrito ningn as u u cdigo que pase la prueba), pero... en realidad no fall, en su lugar se produjo o o un error. Hay una sutil pero importante diferencia. Una prueba unitaria puede terminar con tres tipos de valores: pasar la prueba, fallar y error. Pasar la prueba signica que el cdigo hace lo que se espera. fallar es lo que hac o a la primera prueba que hicimos (hasta que escribimos el cdigo que permit o a pasar la prueba) ejecutaba el cdigo pero el resultado no era el esperado. o error signica que el cdigo ni siquiera se ejecut apropiadamente. o o 2. Lnea 13: Porqu el cdigo no se ejecut correctamente? La traza del error e o o lo indica. El mdulo que ests probando no dispone de una excepcin deo a o nominada OutOfRangeError. Recuerda, has pasado esta excepcin al mtodo o e assertRaises() porque es la excepcin que quieres que que la funcin eleve cuano o do se le pasa una entrada fuera del rango vlido de valores. Pero la excepcin a o an no existe, por lo que la llamada al mtodo assertRaises() falla. Ni siquiera u e ha tenido la oportunidad de probar el funcionamiento de la funcin to roman(); o no llega tan lejos.

202

CAP ITULO 9. PRUEBAS UNITARIAS

Para resolver este problema necesitas denir la excepcin OutOfRangeError en o el chero roman2.py.
1 2 c l a s s OutOfRangeError ( V a l u e E r r o r ) : pass

1. Lnea 1: Las excepciones son clases. Un error fuera de rango es un tipo de error del valor el parmetro est fuera del rango de los valores aceptables. a a Por ello esta excepcin hereda de la excepcin propia de Python ValueError. o o Esto no es estrictamente necesario (podr heredar directamente de la clase a Exception), pero parece ms correcto. a 2. Lnea 2: Las excepciones no suelen hacer nada, pero necesitas al menos una l nea de cdigo para crear una clase. Al llamar a la sentencia pass conseguimos o que no se haga nada y que exista la l nea de cdigo necesaria, as que ya o tenemos creada la clase de la excepcin. o Ahora podemos intentar ejecutar la suite de pruebas de nuevo.
1 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 r o m a n t e s t 2 . py v 2 t e s t t o r o m a n k n o w n v a l u e s ( main . KnownValues ) 3 to roman s h o u l d g i v e known r e s u l t with known i n p u t . . . ok 4 t e s t t o o l a r g e ( main . ToRomanBadInput ) 5 to roman d e b e r f a l l a r con una e n t r a d a muy grande . . . FAIL a 6 7 ====================================================================== 8 FAIL : to roman d e b e r f a l l a r con una e n t r a d a muy grande a 9 10 Traceback ( most r e c e n t c a l l l a s t ) : 11 F i l e r o m a n t e s t 2 . py , l i n e 7 8 , in t e s t t o o l a r g e 12 s e l f . a s s e r t R a i s e s ( roman2 . OutOfRangeError , roman2 . to roman , 4 0 0 0 ) 13 A s s e r t i o n E r r o r : OutOfRangeError not r a i s e d by to roman 14 15 16 Ran 2 t e s t s in 0 . 0 1 6 s 17 18 FAILED ( f a i l u r e s =1)

1. Lnea 5: An no pasa la nueva prueba, pero ya no devuelve un error. En u su lugar, la prueba falla. Es un avance! Signica que la llamada al mtodo e assertRaises() se complet con xito esta vez, y el entorno de pruebas unitarias o e realmente comprob el funcionamiento de la funcin to roman(). o o 2. Lnea 13: Es evidente que la funcin to roman() no eleva la excepcin OutO o o fRangeError que acabas de denir, puesto que an no se lo has dicho. Son u

9.3. PARA Y PRENDELE FUEGO

203

noticias excelentes! signica que es una prueba vlida falla antes de que a escribas el cdigo que hace falta para que pueda pasar. o Ahora toca escribir el cdigo que haga pasar esta prueba satisfactoriamente. o
1 2 3 4 5 6 7 8 9 10 11 def to roman ( n ) : c o n v e r t i n t e g e r t o Roman numeral i f n > 3999: r a i s e OutOfRangeError ( number out o f r a n g e ( must be l e s s than 4 0 0 0 ) ) result = f o r numeral , i n t e g e r in roman numeral map : while n >= i n t e g e r : r e s u l t += numeral n = i n t e g e r return r e s u l t

1. Lnea 4: Es inmediato: si el valor de entrada (n) es mayor de 3999, eleva la excepcin OutOfRangeError. El caso de prueba no valida la cadena de texto o que contiene la excepcin, aunque podr escribir otra prueba que hiciera eso o as (pero ten cuidado con los problemas de internacionalizacin de cadenas que o pueden hacer que var en funcin del idioma del usuario y de la conguracin en o o del equipo). Pasar ahora la prueba? vemoslo. a a
1 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 r o m a n t e s t 2 . py v 2 t e s t t o r o m a n k n o w n v a l u e s ( main . KnownValues ) 3 to roman s h o u l d g i v e known r e s u l t with known i n p u t . . . ok 4 t e s t t o o l a r g e ( main . ToRomanBadInput ) 5 to roman s h o u l d f a i l with l a r g e i n p u t . . . ok 6 7 8 Ran 2 t e s t s in 0 . 0 0 0 s 9 10 OK

1. Lnea 5: Bien, pasan las dos pruebas. Al haber trabajado iterativamente, yendo y viniendo entre la prueba y el cdigo, puedes estar seguro de que las dos l o neas de cdigo que acabas de escribir son las causantes de que una de las pruebas o pasara de fallar a pasar. Esta clase de conanza no es gratis, pero revierte por s misma conforme vas desarrollando ms y ms cdigo. a a o

204

CAP ITULO 9. PRUEBAS UNITARIAS

9.4.

Ms paradas, ms fuego a a

Adems de probar con nmeros que son demasiado grandes, tambin es necea u e sario probar con nmeros demasiado pequeos. Como indicamos al comienzo, en los u n requisitos funcionales, los nmeros romanos no pueden representar ni el cero, ni los u nmeros negativos. u
1 >>> import roman2 2 >>> roman2 . to roman ( 0 ) 3 4 >>> roman2 . to roman ( 1) 5

No est bien, vamos a aadir pruebas para cada una de estas condiciones. a n
1 2 3 4 5 6 7 8 9 10 11 12 c l a s s ToRomanBadInput ( u n i t t e s t . TestCase ) : def t e s t t o o l a r g e ( s e l f ) : to roman s h o u l d f a i l with l a r g e i n p u t s e l f . a s s e r t R a i s e s ( roman3 . OutOfRangeError , roman3 . to roman , 4 0 0 0 ) def t e s t z e r o ( s e l f ) : to roman s h o u l d f a i l with 0 i n p u t s e l f . a s s e r t R a i s e s ( roman3 . OutOfRangeError , roman3 . to roman , 0 ) def t e s t n e g a t i v e ( s e l f ) : to roman s h o u l d f a i l with n e g a t i v e i n p u t s e l f . a s s e r t R a i s e s ( roman3 . OutOfRangeError , roman3 . to roman ,

1)

1. Lnea 4: El mtodo test too large() no ha cambiado desde el paso anterior. Se e muestra aqu para ensear el sitio en el que se incorpora el nuevo cdigo. n o 2. Lnea 8: Aqu hay una nueva prueba: el mtodo test zero(). Como el mtodo e e anterior, indica al mtodo assertRaises(), denido en unittest.TestCase, que llae me a nuestra funcin to roma() con el parmetro 0, y valida que se eleve la o a excepcin correcta, OutOfRangeError. o e 3. Lnea 12: El mtodo test negative() es casi idntico, excepto que pasa -1 a la e funcin to roman(). Si alguna d estas pruebas no eleva una excepcin OutOo o fRangeError (O porque la funcin retorna un valor o porque eleva otra excepo cin), se considera que la prueba ha fallado. o Vamos a comprobar que la prueba falla:

9.4. MAS PARADAS, MAS FUEGO

205

1 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 r o m a n t e s t 3 . py v 2 t e s t t o r o m a n k n o w n v a l u e s ( main . KnownValues ) 3 to roman s h o u l d g i v e known r e s u l t with known i n p u t . . . ok 4 t e s t n e g a t i v e ( main . ToRomanBadInput ) 5 to roman s h o u l d f a i l with n e g a t i v e i n p u t . . . FAIL 6 t e s t t o o l a r g e ( main . ToRomanBadInput ) 7 to roman s h o u l d f a i l with l a r g e i n p u t . . . ok 8 t e s t z e r o ( main . ToRomanBadInput ) 9 to roman s h o u l d f a i l with 0 i n p u t . . . FAIL 10 11 ====================================================================== 12 FAIL : to roman s h o u l d f a i l with n e g a t i v e i n p u t 13 14 Traceback ( most r e c e n t c a l l l a s t ) : 15 F i l e r o m a n t e s t 3 . py , l i n e 8 6 , in t e s t n e g a t i v e 16 s e l f . a s s e r t R a i s e s ( roman3 . OutOfRangeError , roman3 . to roman , 1) 17 A s s e r t i o n E r r o r : OutOfRangeError not r a i s e d by to roman 18 19 ====================================================================== 20 FAIL : to roman s h o u l d f a i l with 0 i n p u t 21 22 Traceback ( most r e c e n t c a l l l a s t ) : 23 F i l e r o m a n t e s t 3 . py , l i n e 8 2 , in t e s t z e r o 24 s e l f . a s s e r t R a i s e s ( roman3 . OutOfRangeError , roman3 . to roman , 0 ) 25 A s s e r t i o n E r r o r : OutOfRangeError not r a i s e d by to roman 26 27 28 Ran 4 t e s t s in 0 . 0 0 0 s 29 30 FAILED ( f a i l u r e s =2)

Estupendo, ambas pruebas fallan como se esperaba. Ahora vamos a volver al cdigo a ver lo que podemos hacer para que pasen las pruebas. o
1 2 3 4 5 6 7 8 9 10 11 def to roman ( n ) : c o n v e r t i n t e g e r t o Roman numeral i f not ( 0 < n < 4 0 0 0 ) : r a i s e OutOfRangeError ( number out o f r a n g e ( must be 1 . . 3 9 9 9 ) ) result = f o r numeral , i n t e g e r in roman numeral map : while n >= i n t e g e r : r e s u l t += numeral n = i n t e g e r return r e s u l t

1. Lnea 3: Esta es una forma muy Phytnica de hacer las cosas: dos compara o ciones a la vez. Es equivalente a if not ((0n) and (n400)), pero es mucho ms a

206

CAP ITULO 9. PRUEBAS UNITARIAS fcil de leer. Esta l a nea de cdigo deber capturar todas las entradas que sean o a demasiado grandes, negativas o cero.

2. Lnea 4: Si cambias las condiciones, asegrate de actualizar las cadenas de u texto para que coincidan con la nueva condicin. Al paquete unittest no le o importar, pero ser ms dif depurar el cdigo si lanza excepciones que a a a cil o estn descritas de forma incorrecta. a Podr mostrarte una serie completa de ejemplos sin relacionar para enseara n te cmo funcionan las comparaciones mltiples, pero en vez de eso, simplemente o u ejecutar unas pruebas unitarias y lo probar. e e
1 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 r o m a n t e s t 3 . py v 2 t e s t t o r o m a n k n o w n v a l u e s ( main . KnownValues ) 3 to roman s h o u l d g i v e known r e s u l t with known i n p u t . . . ok 4 t e s t n e g a t i v e ( main . ToRomanBadInput ) 5 to roman s h o u l d f a i l with n e g a t i v e i n p u t . . . ok 6 t e s t t o o l a r g e ( main . ToRomanBadInput ) 7 to roman s h o u l d f a i l with l a r g e i n p u t . . . ok 8 t e s t z e r o ( main . ToRomanBadInput ) 9 to roman s h o u l d f a i l with 0 i n p u t . . . ok 10 11 12 Ran 4 t e s t s in 0 . 0 1 6 s 13 14 OK

9.5.

Y una cosa ms... a

Hab un requisito funcional ms para convertir nmeros a nmeros romanos: a a u u tener en cuenta a los nmeros no enteros. u
1 >>> import roman3 2 >>> roman3 . to roman ( 0 . 5 ) 3 4 >>> roman3 . to roman ( 1 . 0 ) 5 I

1. Lnea 2: Oh! qu mal. e 2. Lnea 4: Oh! incluso peor. Ambos casos deber lanzar una excepcin. En an o vez de ello, retornan valores falsos.

9.5. Y UNA COSA MAS...

207

La validacin sobre los no enteros no es dif Primero dene una excepcin o cil. o NotIntegerError.
1 # roman4 . py 2 c l a s s OutOfRangeError ( V a l u e E r r o r ) : pass 3 c l a s s N o t I n t e g e r E r r o r ( V a l u e E r r o r ) : pass

Lo siguiente es escribir un caso de prueba que compruebe si se lanza una excepcin NotIntegerError. o
1 2 3 4 5 6 7 c l a s s ToRomanBadInput ( u n i t t e s t . TestCase ) : . . . def t e s t n o n i n t e g e r ( s e l f ) : to roman s h o u l d f a i l with non i n t e g e r i n p u t s e l f . a s s e r t R a i s e s ( roman4 . N o t I n t e g e r E r r o r , roman4 . to roman , 0 . 5 )

Ahora vamos a validar si la prueba falla apropiadamente.


1 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 r o m a n t e s t 4 . py v 2 t e s t t o r o m a n k n o w n v a l u e s ( main . KnownValues ) 3 to roman s h o u l d g i v e known r e s u l t with known i n p u t . . . ok 4 t e s t n e g a t i v e ( main . ToRomanBadInput ) 5 to roman s h o u l d f a i l with n e g a t i v e i n p u t . . . ok 6 t e s t n o n i n t e g e r ( main . ToRomanBadInput ) 7 to roman s h o u l d f a i l with non i n t e g e r i n p u t . . . FAIL 8 t e s t t o o l a r g e ( main . ToRomanBadInput ) 9 to roman s h o u l d f a i l with l a r g e i n p u t . . . ok 10 t e s t z e r o ( main . ToRomanBadInput ) 11 to roman s h o u l d f a i l with 0 i n p u t . . . ok 12 13 ====================================================================== 14 FAIL : to roman s h o u l d f a i l with non i n t e g e r i n p u t 15 16 Traceback ( most r e c e n t c a l l l a s t ) : 17 F i l e r o m a n t e s t 4 . py , l i n e 9 0 , in t e s t n o n i n t e g e r 18 s e l f . a s s e r t R a i s e s ( roman4 . N o t I n t e g e r E r r o r , roman4 . to roman , 0 . 5 ) 19 A s s e r t i o n E r r o r : N o t I n t e g e r E r r o r not r a i s e d by to roman 20 21 22 Ran 5 t e s t s in 0 . 0 0 0 s 23 24 FAILED ( f a i l u r e s =1)

Escribe ahora el cdigo que haga que se pase la prueba. o

208
1 2 3 4 5 6 7 8 9 10 11 12 13

CAP ITULO 9. PRUEBAS UNITARIAS

def to roman ( n ) : c o n v e r t i n t e g e r t o Roman numeral i f not ( 0 < n < 4 0 0 0 ) : r a i s e OutOfRangeError ( number out o f r a n g e ( must be 1 . . 3 9 9 9 ) ) i f not i s i n s t a n c e ( n , i n t ) : r a i s e N o t I n t e g e r E r r o r ( non i n t e g e r s can not be c o n v e r t e d ) result = f o r numeral , i n t e g e r in roman numeral map : while n >= i n t e g e r : r e s u l t += numeral n = i n t e g e r return r e s u l t

1. L nea 5: La funcin interna de Python isinstance() comprueba si una variable o es de un determinado tipo (o tcnicamente, de cualquier tipo descendiente). e 2. Lnea 6: Si el parmetro n no es int elevar la nueva excepcin NotIntegerError. a a o Por ultimo, vamos a comprobar si realmente el cdigo pasa las pruebas. o
1 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 r o m a n t e s t 4 . py v 2 t e s t t o r o m a n k n o w n v a l u e s ( main . KnownValues ) 3 to roman s h o u l d g i v e known r e s u l t with known i n p u t . . . ok 4 t e s t n e g a t i v e ( main . ToRomanBadInput ) 5 to roman s h o u l d f a i l with n e g a t i v e i n p u t . . . ok 6 t e s t n o n i n t e g e r ( main . ToRomanBadInput ) 7 to roman s h o u l d f a i l with non i n t e g e r i n p u t . . . ok 8 t e s t t o o l a r g e ( main . ToRomanBadInput ) 9 to roman s h o u l d f a i l with l a r g e i n p u t . . . ok 10 t e s t z e r o ( main . ToRomanBadInput ) 11 to roman s h o u l d f a i l with 0 i n p u t . . . ok 12 13 14 Ran 5 t e s t s in 0 . 0 0 0 s 15 16 OK

La funcin to roman pasa todas las pruebas y no se me ocurren nuevas pruebas, o por lo que es el momento de pasar a la funcin from roman() o

9.6.

Una agradable simetr a

Convertir una cadena de texto que representa un nmero romano a entero u parece ms dif que convertir un entero en un nmero romano. Es cierto que existe a cil u

9.6. UNA AGRADABLE SIMETR IA

209

el tema de la validacin. Es fcil validar si un nmero entero es mayor que cero, pero o a u un poco ms dif comprobar si una cadena es un nmero romano vlido. Pero ya a cil u a hab amos construido una expresin regular para comprobar nmeros romanos, por o u lo que esa parte est hecha. a Eso nos deja con el problema de convertir la cadena de texto. Como veremos en un minuto, gracias a la rica estructura de datos que denimos para mapear los nmeros romanos a nmeros enteros, el ncleo de la funcin from roman() es tan u u u o simple como el de la funcin to roman(). o Pero primero hacemos las puertas. Necesitaremos una prueba de valores vlidos a conocidos para comprobar la precisin de la funcin. Nuestro juego de pruebas ya o o contiene una mapa de valores conocidos; vamos a reutilizarlos.
1 2 3 4 5 6 7 8 ... def t e s t f r o m r o m a n k n o w n v a l u e s ( s e l f ) : from roman s h o u l d g i v e known r e s u l t with known i n p u t f o r i n t e g e r , numeral in s e l f . known values : r e s u l t = roman5 . from roman ( numeral ) s e l f . assertEqual ( integer , result ) ...

Existe una agradable simetr aqu Las funciones to roman() y from roman() a . son la inversa una de la otra. La primera convierte nmeros enteros a cadenas u formateadas que representan nmeros romanos, la segunda convierte cadenas de u texto que representan a nmeros romanos a nmeros enteros. En teor deber u u a amos ser capaces de hacer ciclos con un nmero pasndolo a la funcin to roman() para u a o recuperar una cadena de texto, luego pasar esa cadena a la funcin from roman() o para recuperar un nmero entero y nalizar con el mismo nmero que comenzamos. u u
1 n = from roman ( to roman ( n ) ) f o r a l l v a l u e s o f n

En este caso all values signica cualquier nmero entre el 1..3999, puesto u que es el rango vlido de entradas a la funcin to roman(). Podemos expresar esa o ta simetr en un caso de prueba que recorrar todos los valores 1..3999, llamar a a to roman(), llamar a from roman() y comprobar que el resultado es el mismo que la entrada original.
1 2 3 4 5 6 7 c l a s s RoundtripCheck ( u n i t t e s t . TestCase ) : def t e s t r o u n d t r i p ( s e l f ) : from roman ( to roman ( n))==n f o r a l l n f o r i n t e g e r in r a n g e ( 1 , 4 0 0 0 ) : numeral = roman5 . to roman ( i n t e g e r ) r e s u l t = roman5 . from roman ( numeral ) s e l f . assertEqual ( integer , result )

210

CAP ITULO 9. PRUEBAS UNITARIAS

Estas pruebas ni siquiera fallarn. No hemos denido an la funcin from roman() a u o por lo que unicamente se elevarn errores. a
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 r o m a n t e s t 5 . py E.E . . . . ====================================================================== ERROR: t e s t f r o m r o m a n k n o w n v a l u e s ( main . KnownValues ) from roman s h o u l d g i v e known r e s u l t with known i n p u t Traceback ( most r e c e n t c a l l l a s t ) : F i l e r o m a n t e s t 5 . py , l i n e 7 8 , in t e s t f r o m r o m a n k n o w n v a l u e s r e s u l t = roman5 . from roman ( numeral ) A t t r i b u t e E r r o r : module o b j e c t has no a t t r i b u t e from roman

Traceback ( most r e c e n t c a l l l a s t ) : F i l e r o m a n t e s t 5 . py , l i n e 1 0 3 , in t e s t r o u n d t r i p r e s u l t = roman5 . from roman ( numeral ) A t t r i b u t e E r r o r : module o b j e c t has no a t t r i b u t e from roman

====================================================================== ERROR: t e s t r o u n d t r i p ( main . RoundtripCheck ) from roman ( to roman ( n))==n f o r a l l n

Ran 7 t e s t s in 0 . 0 1 9 s FAILED ( e r r o r s =2)

Una funcin vac resolver este problema: o a a


1 # roman5 . py 2 def from roman ( s ) : 3 c o n v e r t Roman numeral t o i n t e g e r

(Te has dado cuenta? He denido una funcin simplemente poniendo un docso tring. Esto es vlido en Python. De hecho, algunos programadores lo toman al pie a de la letra. No crees una funcin vac documntala!) o a, e Ahora los casos de prueba s que fallarn. a

9.6. UNA AGRADABLE SIMETR IA


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

211

Traceback ( most r e c e n t c a l l l a s t ) : F i l e r o m a n t e s t 5 . py , l i n e 7 9 , in t e s t f r o m r o m a n k n o w n v a l u e s s e l f . assertEqual ( integer , result ) A s s e r t i o n E r r o r : 1 != None

y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 r o m a n t e s t 5 . py F.F . . . . ====================================================================== FAIL : t e s t f r o m r o m a n k n o w n v a l u e s ( main . KnownValues ) from roman s h o u l d g i v e known r e s u l t with known i n p u t

Traceback ( most r e c e n t c a l l l a s t ) : F i l e r o m a n t e s t 5 . py , l i n e 1 0 4 , in t e s t r o u n d t r i p s e l f . assertEqual ( integer , result ) A s s e r t i o n E r r o r : 1 != None

====================================================================== FAIL : t e s t r o u n d t r i p ( main . RoundtripCheck ) from roman ( to roman ( n))==n f o r a l l n

Ran 7 t e s t s in 0 . 0 0 2 s FAILED ( f a i l u r e s =2)

Ahora ya podemos escribir la funcin from roman(). o


1 2 3 4 5 6 7 8 9 def from roman ( s ) : c o n v e r t Roman numeral t o i n t e g e r result = 0 index = 0 f o r numeral , i n t e g e r in roman numeral map : while s [ i n d e x : i n d e x+l e n ( numeral ) ] == numeral : r e s u l t += i n t e g e r i n d e x += l e n ( numeral ) return r e s u l t

1. Lnea 6: El patrn aqu es el mismo que el de la funcin to roman(). Iteras a o o travs de la estructura de datos de nmeros romanos (la tupla de tuplas), pero e u en lugar de encontrar el mayor nmero entero tantas veces como sea posible, u compruebas coincidencias del carcter romano ms alto tantas veces como a a sea posible. Si no te queda claro cmo funciona from roman() aade una sentencia print al o n nal del bucle while:

212

CAP ITULO 9. PRUEBAS UNITARIAS

1 def from roman ( s ) : 2 c o n v e r t Roman numeral t o i n t e g e r 3 result = 0 4 index = 0 5 f o r numeral , i n t e g e r in roman numeral map : 6 while s [ i n d e x : i n d e x+l e n ( numeral ) ] == numeral : 7 r e s u l t += i n t e g e r 8 i n d e x += l e n ( numeral ) 9 print ( found , numeral , o f l e n g t h , l e n ( numeral ) , , adding , i n t e g e r ) 10 >>> import roman5 11 >>> roman5 . from roman ( MCMLXXII ) 12 found M o f l e n g t h 1 , adding 1000 13 found CM o f l e n g t h 2 , adding 900 14 found L o f l e n g t h 1 , adding 50 15 found X o f l e n g t h 1 , adding 10 16 found X o f l e n g t h 1 , adding 10 17 found I o f l e n g t h 1 , adding 1 18 found I o f l e n g t h 1 , adding 1 19 1972

Es el momento de volver a ejecutar las pruebas.


1 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 r o m a n t e s t 5 . py 2 ....... 3 4 Ran 7 t e s t s in 0 . 0 6 0 s 5 6 OK

Tenemos dos buenas noticias aqu Por un lado que la funcin from roman() . o pasa las pruebas ante entradas vlidas, al menos para los valores conocidos. Por a otro, la prueba de ida y vuelta tambin ha pasado. Combinada con las pruebas e de valores conocidos, puedes estar razonablemente seguro de que ambas funciones a to roman() y from roman() funcionan correctamente para todos los valores vlidos (Esto no est garantizado: es tericamente posible que la funcin to roman() tua o o viera un fallo que produjera un valor errneo de nmero romano y que la funcin o u o from roman() tuviera un fallo rec proco que produjera el mismo valor errneo como o nmero entero. Dependiendo de la aplicacin y de los requisitos, esto puede ser ms u o a o menos preocupante; si es as hay que escribir un conjunto de casos de prueba , mayor hasta que puedas quedar razonablemente traquilo en cuanto a la abilidad del cdigo desarrollado). o

9.7. MAS ENTRADAS ERRONEAS

213

9.7.

Ms entradas errneas a o

Ahora que la funcin from roman() funciona correctamente con entradas vlio a das es el momento de poner la ultima pieza del puzzle: hacer que funcione correcta mente con entradas incorrectas. Eso signica encontrar un modo de mirar una cadena para determinar si es un nmero romano correcto. Esto es inherentemente ms dif u a cil que validar las entradas numricas de la funcin to roman(), pero dispones de una e o poderosa herramienta: las expresiones regulares (si no ests familiarizado con ellas, a ahora es un buen momento para leer el cap tulo sobre las expresiones regulares). Como viste en el caso de estudio: nmeros romanos, existen varias reglas simu ples para construir un nmero romano utilizando las letras M, D, C, L, X, V e I. u Vamos a revisar las reglas: Algunas veces los caracteres son aditivos, I es 1, II es 2 y III es 3. VI es 6 (literalmente 5 + 1), VII es 7 (5+1+1) y XVIII es 18 (10+5+1+1+1). Los caracteres que representan unidades, decenas, centenas y unidades de millar (I, X, C y M) pueden aparecer juntos hasta tres veces como mximo. Para a el 4 debes restar del carcter V, L D (cinco, cincuenta, quinientos) que se a o encuentre ms prximo a la derecha. No se puede representar el cuatro como a o IIII, en su lugar hay que poner IV (5-1). El nmero 40 se representa como XL u (10 menos que 50: 50-10). 41 = XLI, 42 = XLII, 43 = XLIII y luego 44 = XLIV (diez menos que cincuenta ms uno menos que cinco: 50-10+5-1). a De forma similar, para el nmero 9, debes restar del nmero siguiente ms u u a prximo que represente unidades, decenas, centenas o unidades de millar (I, o X, C y M). 8 = VIII, pero 9 = IX (1 menos que 10), no 9 = VIIII puesto que el carcter I no puede repetirse cuatro veces seguidas. El nmero 90 se representa a u con XC y el 900 con CM. Los caracteres V, L y D no pueden repetirse; el nmero 10 siempre se representa u como X y no como VV. El nmero 100 siempre se representa como C y nunca u como LL. Los nmeros romanos siempre se escriben de los caracteres que representan u valores mayores a los menores y se leen de izquierda a derecha por lo que el orden de los caracteres importa mucho. {DC es el nmero 600; CD otro nmero, u u el 400 (500 - 100). CI es 101, mientras que IC no es un nmero romano vlido u a 2 porque no puedes restar I del C .
2

Para representar el 99 deber usar: XCIL (100 - 10 + 10 - 1) as

214

CAP ITULO 9. PRUEBAS UNITARIAS

As una prueba apropiada podr ser asegurarse de que la funcin from roman() , a o falla cuando pasas una cadena que tiene demasiados caracteres romanos repetidos. Pero, cunto es demasiados ? ...depende del carcter. a a
1 2 3 4 5 6 c l a s s FromRomanBadInput ( u n i t t e s t . TestCase ) : def t e s t t o o m a n y r e p e a t e d n u m e r a l s ( s e l f ) : from roman s h o u l d f a i l with t o o many r e p e a t e d numerals f o r s in ( M M , DD , CCCC , LL , XXXX , VV , I I I I ) : M M s e l f . a s s e r t R a i s e s ( roman6 . InvalidRomanNumeralError , roman6 . from roman , s )

Otra prueba util ser comprobar que ciertos patrones no estn repetidos. Por a a ejemplo, IX es 9, pero IXIX no es vlido nunca. a
1 2 3 4 5 6 7 ... def t e s t r e p e a t e d p a i r s ( s e l f ) : from roman s h o u l d f a i l with r e p e a t e d p a i r s o f numerals f o r s in ( CMCM , CDCD , XCXC , XLXL , IXIX , IVIV ) : s e l f . a s s e r t R a i s e s ( roman6 . InvalidRomanNumeralError , roman6 . from roman , s ) ...

Una tercera prueba podr comprobar que los caracteres aparecen en el ora den correcto, desde el mayor al menor. Por ejemplo, CL es 150, pero LC nunca es vlido, porque el carcter para el 50 nunca puede aparecer antes del carcter del a a a 100. Esta prueba incluye un conjunto no vlido de conjuntos de caracteres elegidos a aleatoriamente: I antes que M, V antes que X, y as sucesivamente.
1 2 3 4 5 6 7 8 ... def t e s t m a l f o r m e d a n t e c e d e n t s ( s e l f ) : from roman s h o u l d f a i l with malformed a n t e c e d e n t s f o r s in ( IIMXCC , VX , DCM , CMM , IXIV , MCMC , XCX , IVI , LM , LD , LC ) : s e l f . a s s e r t R a i s e s ( roman6 . InvalidRomanNumeralError , roman6 . from roman , s ) ...

Cada una de estas pruebas se basan en que la funcin from roman() eleve una o excepcin, InvalidRomanNumeralError, que an no hemos denido. o u
1 # roman6 . py 2 c l a s s InvalidRomanNumeralError ( V a l u e E r r o r ) : pass

Las tres pruebas deber fallar, puesto que from roman() an no efecta an u u ningn tipo de validacin de la entrada (Si no fallan ahora, qu demonios estn u o e a comprobando?).

9.7. MAS ENTRADAS ERRONEAS


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

215

Traceback ( most r e c e n t c a l l l a s t ) : F i l e r o m a n t e s t 6 . py , l i n e 1 1 3 , in t e s t m a l f o r m e d a n t e c e d e n t s s e l f . a s s e r t R a i s e s ( roman6 . InvalidRomanNumeralError , roman6 . from roman , s ) A s s e r t i o n E r r o r : InvalidRomanNumeralError not r a i s e d by from roman

y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 r o m a n t e s t 6 . py FFF . . . . . . . ====================================================================== FAIL : t e s t m a l f o r m e d a n t e c e d e n t s ( main . FromRomanBadInput ) from roman s h o u l d f a i l with malformed a n t e c e d e n t s

Traceback ( most r e c e n t c a l l l a s t ) : F i l e r o m a n t e s t 6 . py , l i n e 1 0 7 , in t e s t r e p e a t e d p a i r s s e l f . a s s e r t R a i s e s ( roman6 . InvalidRomanNumeralError , roman6 . from roman , s ) A s s e r t i o n E r r o r : InvalidRomanNumeralError not r a i s e d by from roman

====================================================================== FAIL : t e s t r e p e a t e d p a i r s ( main . FromRomanBadInput ) from roman s h o u l d f a i l with r e p e a t e d p a i r s o f numerals

Traceback ( most r e c e n t c a l l l a s t ) : F i l e r o m a n t e s t 6 . py , l i n e 1 0 2 , in t e s t t o o m a n y r e p e a t e d n u m e r a l s s e l f . a s s e r t R a i s e s ( roman6 . InvalidRomanNumeralError , roman6 . from roman , s ) A s s e r t i o n E r r o r : InvalidRomanNumeralError not r a i s e d by from roman

====================================================================== FAIL : t e s t t o o m a n y r e p e a t e d n u m e r a l s ( main . FromRomanBadInput ) from roman s h o u l d f a i l with t o o many r e p e a t e d numerals

Ran 10 t e s t s in 0 . 0 5 8 s FAILED ( f a i l u r e s =3)

Est bien. Ahora lo que necesitamos es aadir la expresin regular para coma n o probar nmeros romanos vlidos en la funcin from roman(). u a o

216
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

CAP ITULO 9. PRUEBAS UNITARIAS

roman numeral pattern = re . compile ( # beginning of s t r i n g M{ 0 , 3 } # t h o u s a n d s 0 t o 3 Ms (CM|CD|D?C{ 0 , 3 } ) # hundreds 900 (CM) , 400 (CD) , 0 300 ( 0 t o 3 Cs ) , # o r 500 800 (D, f o l l o w e d by 0 t o 3 Cs ) (XC| XL | L?X{ 0 , 3 } ) # t e n s 90 (XC) , 40 (XL) , 0 30 ( 0 t o 3 Xs ) , # o r 50 80 (L , f o l l o w e d by 0 t o 3 Xs ) ( IX | IV |V? I { 0 , 3 } ) # o n e s 9 ( IX ) , 4 ( IV ) , 03 ( 0 t o 3 I s ) , # o r 58 (V, f o l l o w e d by 0 t o 3 I s ) $ # end o f s t r i n g , r e .VERBOSE) def from roman ( s ) : c o n v e r t Roman numeral t o i n t e g e r i f not r o m a n n u m e r a l p a t t e r n . s e a r c h ( s ) : r a i s e InvalidRomanNumeralError ( I n v a l i d Roman numeral : {0} . format ( s ) ) result = 0 index = 0 f o r numeral , i n t e g e r in roman numeral map : while s [ i n d e x : i n d e x + l e n ( numeral ) ] == numeral : r e s u l t += i n t e g e r i n d e x += l e n ( numeral ) return r e s u l t

Y ahora a ejecutar de nuevo las pruebas...


1 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 r o m a n t e s t 7 . py 2 .......... 3 4 Ran 10 t e s t s in 0 . 0 6 6 s 5 6 OK

Y el premio al anticlimax del ao va para... la palabra OK, que se imprime n por parte del mdulo unittest cuando todas pruebas pasan correctamente. o

Cap tulo 10 Refactorizar


Nivel de dicultad:  Despues de haber tocado una vasta cantidad de notas y ms notas. a es la simplicidad la que emerge como la recompensa coronada del arte. Frdric Chopin e e

10.1.

Inmersin o

A pesar tus mejores esfuerzos para escribir un conjunto amplio de pruebas uitarias, tendrs errores (bugs). Qu signica que haya un error? Que nos falta un a e caso de prueba por escribir.
1 >>> import roman7 2 >>> roman7 . from roman ( ) 3 0

1. Lnea 2: Esto es un error. Una cadena vac deber elevar la excepcin In a a o validRomanNumeralError, como cualquier otra secuencia de caracteres que no represente a un nmero romano vlido. u a Despus de reproducir el error, y antes de arreglarlo, deber escribir un caso e as de prueba que falle para ilustrar el error.

217

218
1 2 3 4 5 6 7 8

CAP ITULO 10. REFACTORIZAR

c l a s s FromRomanBadInput ( u n i t t e s t . TestCase ) : . . . def t e s t B l a n k ( s e l f ) : from roman s h o u l d f a i l with blank s t r i n g s e l f . a s s e r t R a i s e s ( roman6 . InvalidRomanNumeralError , roman6 . from roman , )

a 1. Lnea 7: Es muy simple. La llamada a from roman() con una cadena vac debe asegurar que eleva la excepcin InvalidRomanNumeralError. La parte ms dif o a cil fue encontrar el error; ahora que lo conoces, crear una prueba que lo reeje es lo fcil. a Puesto que el cdigo tiene un fallo, y dispones de un caso de prueba que o comprueba que existe, el caso de prueba fallar: a
1 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 r o m a n t e s t 8 . py v 2 from roman s h o u l d f a i l with blank s t r i n g . . . FAIL 3 from roman s h o u l d f a i l with malformed a n t e c e d e n t s . . . ok 4 from roman s h o u l d f a i l with r e p e a t e d p a i r s o f numerals . . . ok 5 from roman s h o u l d f a i l with t o o many r e p e a t e d numerals . . . ok 6 from roman s h o u l d g i v e known r e s u l t with known i n p u t . . . ok 7 to roman s h o u l d g i v e known r e s u l t with known i n p u t . . . ok 8 from roman ( to roman ( n))==n f o r a l l n . . . ok 9 to roman s h o u l d f a i l with n e g a t i v e i n p u t . . . ok 10 to roman s h o u l d f a i l with non i n t e g e r i n p u t . . . ok 11 to roman s h o u l d f a i l with l a r g e i n p u t . . . ok 12 to roman s h o u l d f a i l with 0 i n p u t . . . ok 13 14 ====================================================================== 15 FAIL : from roman s h o u l d f a i l with blank s t r i n g 16 17 Traceback ( most r e c e n t c a l l l a s t ) : 18 F i l e r o m a n t e s t 8 . py , l i n e 1 1 7 , in t e s t b l a n k 19 s e l f . a s s e r t R a i s e s ( roman8 . InvalidRomanNumeralError , roman8 . from roman , ) 20 A s s e r t i o n E r r o r : InvalidRomanNumeralError not r a i s e d by from roman 21 22 23 Ran 11 t e s t s in 0 . 1 7 1 s 24 25 FAILED ( f a i l u r e s =1) 26 Now you can f i x t h e bug . 27 28 s k i p o v e r t h i s code l i s t i n g 29 30 [ h i d e ] [ open in new window ]

10.1. INMERSION Ahora puedes arreglar el fallo.


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def from roman ( s ) : c o n v e r t Roman numeral t o i n t e g e r i f not s : r a i s e InvalidRomanNumeralError ( Input can not be blank ) i f not r e . s e a r c h ( romanNumeralPattern , s ) : r a i s e InvalidRomanNumeralError ( I n v a l i d Roman numeral : {} . format ( s ) ) result = 0 index = 0 f o r numeral , i n t e g e r in romanNumeralMap : while s [ i n d e x : i n d e x+l e n ( numeral ) ] == numeral : r e s u l t += i n t e g e r i n d e x += l e n ( numeral ) return r e s u l t

219

1. Lnea 3: Solamente se necesitan dos l neas de cdigo: una comprobacin expl o o cita por la cadena de texto vac y la sentencia raise. a 2. Lnea 6: Creo que no lo he mencionado en ninguna otra parte del libro, por lo que te puede servir como tu ultima leccin en formateo de cadenas de texto. o Desde Python 3.1 puedes dejar de poner los valors numricos cuando utilizas e ndices posicionales en el especicador de formatos. En lugar de utilizar el especicador de formato {0} para indicar el primer parmetro del mtodo a e format(), puedes utilizar directamente {} y Python lo sustituir por el a ndice posicional apropiado. Esto funciona para cualquier nmero de parmetros; el u a primer {} equivale a {0}, el segundo {} a {1} y as sucesivamente.
1 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 r o m a n t e s t 8 . py v 2 from roman s h o u l d f a i l with blank s t r i n g . . . ok 3 from roman s h o u l d f a i l with malformed a n t e c e d e n t s . . . ok 4 from roman s h o u l d f a i l with r e p e a t e d p a i r s o f numerals . . . ok 5 from roman s h o u l d f a i l with t o o many r e p e a t e d numerals . . . ok 6 from roman s h o u l d g i v e known r e s u l t with known i n p u t . . . ok 7 to roman s h o u l d g i v e known r e s u l t with known i n p u t . . . ok 8 from roman ( to roman ( n))==n f o r a l l n . . . ok 9 to roman s h o u l d f a i l with n e g a t i v e i n p u t . . . ok 10 to roman s h o u l d f a i l with non i n t e g e r i n p u t . . . ok 11 to roman s h o u l d f a i l with l a r g e i n p u t . . . ok 12 to roman s h o u l d f a i l with 0 i n p u t . . . ok 13 14 15 Ran 11 t e s t s in 0 . 1 5 6 s 16 17 OK

220

CAP ITULO 10. REFACTORIZAR

Ahora la prueba de la cadena vac pasa sin problemas, por lo que el error a est arreglado. Adems, todas las dems pruebas siguen funcionando, lo que signica a a a que la reparacin del error no rompe nada de lo que ya funcionaba, para lo que o disponemos de pruebas previas. Se ha acabado el trabajo. Codicar de esta forma no hace que sea ms sencillo arreglar los errores. Los a errores sencillos (como este caso) requieren casos de prueba sencillos: los errores complejos requerirn casos de prueba complejos. A primera vista, puede parecer a que llevar ms tiempo reparar un error en un entorno de desarrollo orientado a a a pruebas, puesto que necesitas expresar en cdigo aquello que reeja el error (para o poder escribir el caso de prueba) y luego arreglar el error propiamente dicho. Lo luego si el caso de prueba no pasa satisfactoriamente necesitas arreglar lo que sea que est roto o sea errneo o, incluso, que el caso de prueba tenga un error en e o s mismo. Sin embargo, a largo plazo, esta forma de trabajar entre el cdigo y la o prueba resulta rentable, porque hace ms probable que los errores sean arreglados a correctamente sin romper otra cosa. Una caso de prueba escrito hoy, es una prueba de regresin para el d de maana. o a n

10.2.

Gestionar requisitos cambiantes

A pesar de tus mayores esfuerzos para clavar a la tierra a tus clientes e identicar los requisitos exactos a fuerza de hacerles cosas horribles con tijeras y cera caliente, los requisitos cambiarn. La mayor de los clientes no saben lo que a a quieren hasta que lo ven, e incluso aunque lo supieran, no es fcil para ellos articular a de forma precisa lo que quieren de forma que nos sea util a los desarrolladores. E incluso en ese caso, querrn ms cosas para la siguiente versin del proyecto. Por eso, a a o preprate para actualizar los casos de prueba segn van cambiando los requisitos. a u Imagina, por ejemplo, que quer ampliar el rango de las funciones de converas sin de nmeros romanos. Normalmente, ningn carcter de un nmero romano se o u u a u puede repetir ms de tres veces seguidas. Pero los romano estaban deseando hacer a una excepcin a a la regla para poder reejar el nmero 4000 con cuatro M seguidas. o u Si haces este cambio, ser posible ampliar el rango de valores vlidos en romano del a a 1..3999 al 1..4999. Pero primero, necesitas modicar los casos de prueba.

10.2. GESTIONAR REQUISITOS CAMBIANTES


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 c l a s s KnownValues ( u n i t t e s t . TestCase ) : known values = ( ( 1 , I ) , . . . ( 3 9 9 9 , MMMCMXCIX ) , ( 4 0 0 0 , M M ) , M M ( 4 5 0 0 , M M D ) , M M ( 4 8 8 8 , MMMMDCCCLXXXVIII ) , ( 4 9 9 9 , MMMMCMXCIX ) )

221

c l a s s ToRomanBadInput ( u n i t t e s t . TestCase ) : def t e s t t o o l a r g e ( s e l f ) : to roman s h o u l d f a i l with l a r g e i n p u t s e l f . a s s e r t R a i s e s ( roman8 . OutOfRangeError , roman8 . to roman , 5 0 0 0 ) . . . c l a s s FromRomanBadInput ( u n i t t e s t . TestCase ) : def t e s t t o o m a n y r e p e a t e d n u m e r a l s ( s e l f ) : from roman s h o u l d f a i l with t o o many r e p e a t e d numerals f o r s in ( M M M , DD , CCCC , LL , XXXX , VV , I I I I ) : MM s e l f . a s s e r t R a i s e s ( roman8 . InvalidRomanNumeralError , roman8 . from roman , s ) . . . c l a s s RoundtripCheck ( u n i t t e s t . TestCase ) : def t e s t r o u n d t r i p ( s e l f ) : from roman ( to roman ( n))==n f o r a l l n f o r i n t e g e r in r a n g e ( 1 , 5 0 0 0 ) : numeral = roman8 . to roman ( i n t e g e r ) r e s u l t = roman8 . from roman ( numeral ) s e l f . assertEqual ( integer , result )

1. Lnea 7: Los valores existentes no cambian (an son valores razonables para u probar), pero necesitas aadir unos cuantos en el rango de 4000. He incluido n el 4000 (el ms corto), 4500 (el segundo ms corto), 4888 (el ms largo) y el a a a 4999 (el mayor). 2. Lnea 15: La denicin de la entrada mayor ha cambiado. Esta prueba se o usaba para probar la funcin to roman() con el nmero 4000 y esperar un error; o u

222

CAP ITULO 10. REFACTORIZAR ahora que los nmeros del rango 4000-4999 son vlidos, necesitamos subir el u a valor de la prueba al 5000.

3. Lnea 24: La denicin de demasiados caracteres repetidos tambin ha cam o e biado. Esta prueba llamaba a la funcin from roman() con la cadena M y o esperaba un error; ahora que MMMM se considera un nmero romano vlido, u a necesitamos elevar el valor a MMMMM. 4. Lnea 35: El ciclo de prueba a travs del rango completo de valores del nmero e u 1 al 3999 tambin hay que cambiarlo. Puesto que el rango se ha expandido es e necesario actualizar el bucle para que llegue hasta el 4999. Ahora los casos de prueba estn actualizados con los nuevos requisitos; pero a el cdigo no lo est, por lo que hay que esperar que los casos de prueba fallen. o a

10.2. GESTIONAR REQUISITOS CAMBIANTES


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 r o m a n t e s t 9 . py from roman s h o u l d f a i l with blank s t r i n g . . . ok from roman s h o u l d f a i l with malformed a n t e c e d e n t s . . . ok from roman s h o u l d f a i l with non s t r i n g i n p u t . . . ok from roman s h o u l d f a i l with r e p e a t e d p a i r s o f numerals . . . ok from roman s h o u l d f a i l with t o o many r e p e a t e d numerals . . . ok from roman s h o u l d g i v e known r e s u l t with known i n p u t . . . ERROR to roman s h o u l d g i v e known r e s u l t with known i n p u t . . . ERROR from roman ( to roman ( n))==n f o r a l l n . . . ERROR to roman s h o u l d f a i l with n e g a t i v e i n p u t . . . ok to roman s h o u l d f a i l with non i n t e g e r i n p u t . . . ok to roman s h o u l d f a i l with l a r g e i n p u t . . . ok to roman s h o u l d f a i l with 0 i n p u t . . . ok

223

====================================================================== ERROR: from roman s h o u l d g i v e known r e s u l t with known i n p u t Traceback ( most r e c e n t c a l l l a s t ) : F i l e r o m a n t e s t 9 . py , l i n e 8 2 , in t e s t f r o m r o m a n k n o w n v a l u e s r e s u l t = roman9 . from roman ( numeral ) F i l e C: \ home\ d i v e i n t o p y t h o n 3 \ examples \roman9 . py , l i n e 6 0 , in from roman r a i s e InvalidRomanNumeralError ( I n v a l i d Roman numeral : {0} . format ( s ) ) roman9 . InvalidRomanNumeralError : I n v a l i d Roman numeral : M M M M

====================================================================== ERROR: to roman s h o u l d g i v e known r e s u l t with known i n p u t Traceback ( most r e c e n t c a l l l a s t ) : F i l e r o m a n t e s t 9 . py , l i n e 7 6 , in t e s t t o r o m a n k n o w n v a l u e s r e s u l t = roman9 . to roman ( i n t e g e r ) F i l e C: \ home\ d i v e i n t o p y t h o n 3 \ examples \roman9 . py , l i n e 4 2 , in to roman r a i s e OutOfRangeError ( number out o f r a n g e ( must be 0 . . 3 9 9 9 ) ) roman9 . OutOfRangeError : number out o f r a n g e ( must be 0 . . 3 9 9 9 )

====================================================================== ERROR: from roman ( to roman ( n))==n f o r a l l n Traceback ( most r e c e n t c a l l l a s t ) : F i l e r o m a n t e s t 9 . py , l i n e 1 3 1 , in t e s t S a n i t y numeral = roman9 . to roman ( i n t e g e r ) F i l e C: \ home\ d i v e i n t o p y t h o n 3 \ examples \roman9 . py , l i n e 4 2 , in to roman r a i s e OutOfRangeError ( number out o f r a n g e ( must be 0 . . 3 9 9 9 ) ) roman9 . OutOfRangeError : number out o f r a n g e ( must be 0 . . 3 9 9 9 )

Ran 12 t e s t s in 0 . 1 7 1 s FAILED ( e r r o r s =3)

224

CAP ITULO 10. REFACTORIZAR

1. Lnea 6: La prueba de from roman() sobre valores conocidos falla en cuanto encuentra MMMM, puesto que from roman() an cree que este nmero no es u u vlido. a 2. Lnea 7: La prueba de valores conocidos de to roman() falla en cuanto en cuentra 4000, puesto que to roman() an piensa que este valor est fuera de u a rango. 3. Lnea 8: La prueba completa de ida y vuelta tambin fallar en cuanto e a encuentre el valor 4000, porque to roman() an piensa que est fuera de rango. u a Ahora que tienes casos de prueba que fallan debido a los nuevos requisitos, puedes abordar la modicacin del cdigo para incorporar los mismos de forma que o o se superen los casos de prueba (Cuando comienzas a trabajar de forma orientada a pruebas, puede parecer extrao al principio que el cdigo que se va a probar nunca va n o por delante de los casos de prueba. Mientras est por detrs te queda trabajo a a por hacer, en cuanto el cdigo alcanza a los casos de prueba, has terminado de o codicar. Una vez te acostumbres, de preguntars cmo es posible que hayas estado a o programando en el pasado sin pruebas).

10.2. GESTIONAR REQUISITOS CAMBIANTES


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

225

roman numeral pattern = re . compile ( # beginning of s t r i n g M{ 0 , 4 } # t h o u s a n d s 0 t o 4 Ms (CM|CD|D?C{ 0 , 3 } ) # hundreds 900 (CM) , 400 (CD) , 0 300 ( 0 t o 3 Cs ) , # o r 500 800 (D, f o l l o w e d by 0 t o 3 Cs ) (XC| XL | L?X{ 0 , 3 } ) # t e n s 90 (XC) , 40 (XL) , 0 30 ( 0 t o 3 Xs ) , # o r 50 80 (L , f o l l o w e d by 0 t o 3 Xs ) ( IX | IV |V? I { 0 , 3 } ) # o n e s 9 ( IX ) , 4 ( IV ) , 03 ( 0 t o 3 I s ) , # o r 58 (V, f o l l o w e d by 0 t o 3 I s ) $ # end o f s t r i n g , r e .VERBOSE) def to roman ( n ) : c o n v e r t i n t e g e r t o Roman numeral i f not ( 0 < n < 5 0 0 0 ) : r a i s e OutOfRangeError ( number out o f r a n g e ( must be 1 . . 4 9 9 9 ) ) i f not i s i n s t a n c e ( n , i n t ) : r a i s e N o t I n t e g e r E r r o r ( non i n t e g e r s can not be c o n v e r t e d ) result = f o r numeral , i n t e g e r in roman numeral map : while n >= i n t e g e r : r e s u l t += numeral n = i n t e g e r return r e s u l t def from roman ( s ) : . . .

1. Lnea 3: No necesitas hacer ningn cambio a la funcin from roman(). Lo unico u o que hay que modicar es roman numeral pattern. Si observas detenidamente, lo primero que notars es que he cambiado el valor mximo de caracteres M a a opcionales, para poner 4 donde antes hab 3. Esto permitir la existencia a a e de valores romanos de 4999. La funcin from roman() es totalmente genrica. o simplemente busca caracteres romanos y los va acumulando, sin preocuparse sobre las veces que se repite. La unica razn por la que no permit MMMM o a es que lo imped amos expresamente en la comprobacin contra la expresin o o regular. n 2. Lnea 15: La funcin to roman() solamente necesita un pequeo cambio en o el rango de validacin. Donde se comprobaba que el valor se encontrase en o 0 n 4000, ahora se comprueba que se cumpla 0 n 5000. Y se modica el mensaje de error que se eleva para reejar el nuevo rango (1...4999 en lugar de 1...3999). No necesitas realizar ms cambios a la funcin (Es capaz de a o

226

CAP ITULO 10. REFACTORIZAR aadir una M por cada millar que encuentra. La unica razn por la que antes n o no funcionaba con 4000 es que la validacin del rango de valores vlidos lo o a imped expl a citamente).

Puede ser que ests algo escptico sobre que estos dos pequeos cambios sean e e n todo lo que necesitas. Vale, no me creas, obsrvalo por ti mismo. e
1 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 r o m a n t e s t 9 . py v 2 from roman s h o u l d f a i l with blank s t r i n g . . . ok 3 from roman s h o u l d f a i l with malformed a n t e c e d e n t s . . . ok 4 from roman s h o u l d f a i l with non s t r i n g i n p u t . . . ok 5 from roman s h o u l d f a i l with r e p e a t e d p a i r s o f numerals . . . ok 6 from roman s h o u l d f a i l with t o o many r e p e a t e d numerals . . . ok 7 from roman s h o u l d g i v e known r e s u l t with known i n p u t . . . ok 8 to roman s h o u l d g i v e known r e s u l t with known i n p u t . . . ok 9 from roman ( to roman ( n))==n f o r a l l n . . . ok 10 to roman s h o u l d f a i l with n e g a t i v e i n p u t . . . ok 11 to roman s h o u l d f a i l with non i n t e g e r i n p u t . . . ok 12 to roman s h o u l d f a i l with l a r g e i n p u t . . . ok 13 to roman s h o u l d f a i l with 0 i n p u t . . . ok 14 15 16 Ran 12 t e s t s in 0 . 2 0 3 s 17 18 OK

Pasan todas las pruebas, paramos de codicar. Un conjunto amplio y exhaustivo de pruebas signica que nunca tienes que depender de un programador que dice Conf en mi. a

10.3.

Rectorizacin o

Lo mejor de disponer de un conjunto de pruebas exhaustivo no es la sensacin o agradable que se obtiene cuando todas las pruebas pasan satisfactoriamente,ni la sensacin de xito cuando alguien te culpa de romper su cdigo y pruebas que no o e o has sido t. Lo mejor de las pruebas unitarias es que te dan la libertad de refactorizar u el cdigo sin piedad. o La refactorizacin es el proceso por el que se toma cdigo que funciona correco o tamente y se le hace funcionar mejor. Normalmente mejor signica ms rpido, a a aunque tambin puede signicar usando menos memoria o usando menos disco e o simplemente de forma ms elegante. Independientemente de lo que signique a para ti en tu entorno, refactorizar es importante para la salud a largo plazo de un programa.

10.3. RECTORIZACION

227

Aqu mejor signica dos cosas a la vez: ms rpido y ms fcil de mante a a a a ner. Espec camente, la funcin from roman() es ms lenta y compleja de lo que me o a gustar debido a la fea expresin regular que se utiliza para validar los nmeros roa o u manos. Podr pensar de acuerdo, la expresin regular es grande y compleja, pero as o cmo si no voy a validar que una cadena arbitraria es un nmero romano vlido. o u a Respuesta, unicamente existen 5000, porqu no construir una tabla de bsque e u da? Esta idea se hace cada vez ms evidente cuando se observa que as no hay que a utilizar expresiones regulares. Segn se construye una tabla de bsqueda de nmeu u u ros romanos para convertir enteros en romanos, se puede construir una tabla inversa para convertir de romanos a enteros. As cuando tengas que comprobar si una ca, dena de texto es un nmero romano vlido ya dispondrs de los nmeros vlidos y u a a u a validar queda reducido a mirar en un diccionario de bsqueda. u Y lo mejor de todo es que ya dispones de un buen conjunto de pruebas unitarias. Puedes modicar la mitad del cdigo del mdulo, pero las pruebas seguirn siendo o o a las mismas. Esto signica que puedes comprobar a ti mismo y a los dems que a el nuevo cdigo funciona tan bien como el original. o

228
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58

CAP ITULO 10. REFACTORIZAR

c l a s s OutOfRangeError ( V a l u e E r r o r ) : pass c l a s s N o t I n t e g e r E r r o r ( V a l u e E r r o r ) : pass c l a s s InvalidRomanNumeralError ( V a l u e E r r o r ) : pass roman numeral map = ( ( ( ( ( ( ( ( ( ( ( ( ( ( M , CM D , CD C , XC L , XL X , IX V , IV I , 1000) , , 900) , 500) , , 400) , 100) , , 90) , 50) , , 40) , 10) , , 9) , 5) , , 4) , 1))

t o r o m a n t a b l e = [ None ] f r o m r o m a n t a b l e = {} def to roman ( n ) : c o n v e r t i n t e g e r t o Roman numeral i f not ( 0 < n < 5 0 0 0 ) : r a i s e OutOfRangeError ( number out o f r a n g e ( must be 1 . . 4 9 9 9 ) ) i f i n t ( n ) != n : r a i s e N o t I n t e g e r E r r o r ( non i n t e g e r s can not be c o n v e r t e d ) return t o r o m a n t a b l e [ n ] def from roman ( s ) : c o n v e r t Roman numeral t o i n t e g e r i f not i s i n s t a n c e ( s , s t r ) : r a i s e InvalidRomanNumeralError ( Input must be a s t r i n g ) i f not s : r a i s e InvalidRomanNumeralError ( Input can not be blank ) i f s not in f r o m r o m a n t a b l e : r a i s e InvalidRomanNumeralError ( I n v a l i d Roman numeral : {0} . format ( s ) ) return f r o m r o m a n t a b l e [ s ] def b u i l d l o o k u p t a b l e s ( ) : def to roman ( n ) : result = f o r numeral , i n t e g e r in roman numeral map : i f n >= i n t e g e r : r e s u l t = numeral n = i n t e g e r break if n > 0: r e s u l t += t o r o m a n t a b l e [ n ] return r e s u l t f o r i n t e g e r in r a n g e ( 1 , 5 0 0 0 ) : roman numeral = to roman ( i n t e g e r ) t o r o m a n t a b l e . append ( roman numeral ) f r o m r o m a n t a b l e [ roman numeral ] = i n t e g e r build lookup tables ()

10.3. RECTORIZACION

229

Vamos a trocear el cdigo anterior en partes fciles de digerir. La l o a nea ms a importante es la ultima.
1 build lookup tables ()

Se trata de una llamada a funcin pero no est rodeada de una sentencia if. o a No es un bloque if name == main , esta funcin se llama cuando el mdulo o o se importa (Es importante conocer que los mdulos unicamente se importan una o vez, cuando se cargan en memoria la primera vez que se usan. Si importas de nuevo un mdulo ya cargado, Python no hace nada. Por eso esta funcin unicamente se o o ejecuta la primera vez que importas este mdulo. o Qu es lo que hace la funcin build lookup tables()? me alegor de que me lo e o preguntes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 t o r o m a n t a b l e = [ None ] f r o m r o m a n t a b l e = {} . . . def b u i l d l o o k u p t a b l e s ( ) : def to roman ( n ) : result = f o r numeral , i n t e g e r in roman numeral map : i f n >= i n t e g e r : r e s u l t = numeral n = i n t e g e r break if n > 0: r e s u l t += t o r o m a n t a b l e [ n ] return r e s u l t f o r i n t e g e r in r a n g e ( 1 , 5 0 0 0 ) : roman numeral = to roman ( i n t e g e r ) t o r o m a n t a b l e . append ( roman numeral ) f r o m r o m a n t a b l e [ roman numeral ] = i n t e g e r

1. Lnea 7: Este es un trozo muy inteligente de programa, tal vez demasia do. La funcin to roman() se dene en primer lugar; busca valores en la tao bla de bsqueda y las retorna. Pero la funcin build lookup tables() redene u o la funcin para que haga algo (como en el ejemplo anterior, antes de que o aadieras una tabla de bsqueda). Dentro de la funcin build lookup tables() n u o se llama a la funcin to roman(9 redenida en la funcin. Una vez la funcin o o o build lookup tables() naliza, la versin redenida desaparece solamente se o dene en el ambito local de la funcin build lookup tables(). o

230

CAP ITULO 10. REFACTORIZAR

2. Lnea 19: Esta l nea de cdigo llamar a la funcin to roman() redenida, la o a o que realmente calcula el nmero romano. u 3. Lnea 20: Una vez dispones del resultado (de la funcin to roman() redenida), o aaders el valor entero y su equivalente romano a las dos tablas de bsqueda. n u Una vez construidas ambas tablas, el resto del cdigo es simple y rpido. o a
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 def to roman ( n ) : c o n v e r t i n t e g e r t o Roman numeral i f not ( 0 < n < 5 0 0 0 ) : r a i s e OutOfRangeError ( number out o f r a n g e ( must be 1 . . 4 9 9 9 ) ) i f i n t ( n ) != n : r a i s e N o t I n t e g e r E r r o r ( non i n t e g e r s can not be c o n v e r t e d ) return t o r o m a n t a b l e [ n ] def from roman ( s ) : c o n v e r t Roman numeral t o i n t e g e r i f not i s i n s t a n c e ( s , s t r ) : r a i s e InvalidRomanNumeralError ( i f not s : r a i s e InvalidRomanNumeralError ( i f s not in f r o m r o m a n t a b l e : r a i s e InvalidRomanNumeralError ( I n v a l i d Roman numeral : return f r o m r o m a n t a b l e [ s ]

Input must be a s t r i n g ) Input can not be blank )

{0} . format ( s ) )

1. Lnea 7: Despus de efectuar las validaciones de rango, la funcin to roman() e o unicamente tiene que encontrar el valor apropiado en la tabla de bsqueda y u devolverlo. 2. Lnea 17: De forma similar, la funcin from roman() queda reducida a algunas o validaciones de l mites y una l nea de cdigo. No hay expresiones regulares. o No hay bucles. Solamente la conversin desde y hacia nmeros romanos. o u Pero funciona?, s s funciona. Y puedo probarlo. , ,

10.4. SUMARIO

231

1 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 r o m a n t e s t 1 0 . py v 2 from roman s h o u l d f a i l with blank s t r i n g . . . ok 3 from roman s h o u l d f a i l with malformed a n t e c e d e n t s . . . ok 4 from roman s h o u l d f a i l with non s t r i n g i n p u t . . . ok 5 from roman s h o u l d f a i l with r e p e a t e d p a i r s o f numerals . . . ok 6 from roman s h o u l d f a i l with t o o many r e p e a t e d numerals . . . ok 7 from roman s h o u l d g i v e known r e s u l t with known i n p u t . . . ok 8 to roman s h o u l d g i v e known r e s u l t with known i n p u t . . . ok 9 from roman ( to roman ( n))==n f o r a l l n . . . ok 10 to roman s h o u l d f a i l with n e g a t i v e i n p u t . . . ok 11 to roman s h o u l d f a i l with non i n t e g e r i n p u t . . . ok 12 to roman s h o u l d f a i l with l a r g e i n p u t . . . ok 13 to roman s h o u l d f a i l with 0 i n p u t . . . ok 14 15 16 Ran 12 t e s t s in 0 . 0 3 1 s 17 18 OK

1. Lnea 16: S que no lo has preguntado pero, tambin es rpido! Como diez e e a veces ms que antes. Por supuesto, no es una comparacin totalmente justa, a o puesto que esta versin tarda ms en importarse (cuando construye las tablas o a de bsqueda). Pero como el import se realiza una unica vez, el coste de inicio u se amortiza en las llamadas a las funciones from roman() y to roman(). Puesto que las pruebas hacen varios miles de llamadas a las funciones (la prueba de ciclo hace diez mil) se compensa enseguida. La moraleja del cuento? La simplicidad es una virtud. Especialmente cuando se trata de expresiones regulares. Las pruebas unitarias te dan la suciente conanza como para hacer modicaciones a gran escala en el cdigo. o

10.4.

Sumario

La prueba unitaria es un concepto muy valioso que, si se implementa de forma adecuada, puede reducir los costes de mantenimiento e incrementar la exibilidad a largo plazo de cualquier proyecto. Tambin es importante comprender que las e pruebas unitarias no son la panacea, ni un solucionador mgico de problemas, ni a

232

CAP ITULO 10. REFACTORIZAR

un bala de plata. Escribir casos de prueba utiles es una tarea dura y mantenerlos actualizados requiere disciplina (especialmente cuando los clientes estn reclamando a las correcciones de fallos cr ticos). Las pruebas unitarias no sustituyen otras formas de prueba, inclu das las pruebas funcionales, de integracin y de aceptacin por o o parte del usuario. Pero son prcticas y funcionan y, una vez las hayas utilizado, te a preguntars cmo has podido trabajar alguna vez sin ellas. a o Estos pocos cap tulos han cubierto muchos aspectos, gran parte de ellos ni siquiera son espec cos de Python. Existen marcos de trabajo para pruebas unitarias en muchos lenguajes de programacin; todos requieren que comprendas los mismos o conceptos bsicos: a Diseo de casos de prueba que sean espec n cos, automatizados e independientes entre s . Escribir los casos de prueba antes del cdigo que se est probando. o a Escribir casos de prueba que tengan entradas vlidas y comprobar que se a produce el resultado esperado. Escribir casos de prueba con entradas errneas y comprobar que se produce el o fallo esperado. Escribir y actualizar los casos de prueba para reejar nuevos requisitos. Refactorizar sin piedad para mejorar el rendimiento, escalabilidad, legibilidad, mantenibilidad o cualquier otra bilidad que eches de menos.

Cap tulo 11 Ficheros


Nivel de dicultad:   Una caminata de nueve millas no es ninguna broma, especialmente si se hace bajo la lluvia. Harry Kemelman, La caminata de nueve millas

11.1.

Inmersin o

Mi porttil con Windows ten 38.493 cheros antes de instalar ninguna aplia a cacin. Al instalar Python se aadieron unos 3.000 ms. El chero es el paradigma o n a principal de almacenamiento de cualquie sistema operativo importante; este concepto est tan arraigado que la mayor de la gente tendr problemas imaginando a a a una alternativa. Metafricamente hablando, tu ordenador se est ahogndose en o a a cheros.

11.2.

Leer contenido de cheros de texto

Antes de que puedas leer un chero, necesitas abrirlo. En Python, esto no podr ser ms sencillo: a a
1 a f i l e = open ( examples / c h i n e s e . t x t , e n c o d i n g= u t f 8 )

Python dispone de la funcin interna open(), que toma el nombre de un chero o como parmetro. En este ejemplo, el nombre del chero es examples/chinese.txt. a Hay cinco cosas interesantes que resaltar en este nombre de chero: 233

234

CAP ITULO 11. FICHEROS No es unicamente un nombre de chero; es una combinacin de un camino a o travs de un directorio y un nombre de chero. Podr e amos pensar en un funcin o hipottica que podr tomar dos parmetros un camino a un directorio y un e a a nombre de chero pero la funcin open() unicamente toma uno. En Python, o siempre que se necesita un nombre de chero puedes utilizar parte o todo el camino al directorio en el que se encuentre. El camino al directorio utiliza la barra inclinada hacia adelante, independientemente del sistema operativo que se est utilizando. Windows utiliza barras e inclinadas invertidas para desplazarse entre subdirectorios, mientras que Mac OS X y Linux utilizan barras inclinadas hacia adelante. Pero en Python, las barras inclinadas hacia adelante siempre funcionan, incluso en Windows. El camino al directorio no comienza con barra inclinada o letra de unidad, por lo que se trata de un camino relativo. Relativo a qu? podr preguntar... e as paciencia, mi pequeo saltamontes. n Es una cadena. Todos los sistemas operativos modernos (incluso Windows!) utilizan Unicode para almacenar los nombres de los cheros y directorios. Python 3 permite trabajar con caminos y cheros no ASCII. No necesita encontrase en un disco local. Podr tratarse de una unidad de red a montada en tu ordenador. Este chero podr ser un elemento de un sistema a de cheros virtual. Si tu ordenador (sistema operativo) lo considera un chero y puede acceder a l como tal, Python puede abrirlo. e

Pero esta llamada a la funcin open() no se acaba con el nombre del chero. o Existe otro parmetro, denominado encoding. Oh! esto est resultando terriblemente a a familiar.

11.2.1.

La codicacin de caracteres ense a su fea cara o n

Los bytes son bytes; los caracteres son una abstraccin. Una cadena es una o secuencia de caracteres Unicode. Pero un chero en disco no es una secuencia de caracteres Unicode; un chero en disco es una secuencia de caracteres. Por eso si lees un chero de texto del disco, Cmo convierte Python la secuencia de bytes en una o secuencia de caracteres? Decodica los bytes de acuerdo a un algoritmo espec ca de codicacin de caracteres y retorna una secuenca de caracteres Unicode (tambin o e conocida como una cadena de texto).

11.2. LEER CONTENIDO DE FICHEROS DE TEXTO


1 2 3 4 5 6 7 8 9 10 11

235

# E s t e e j e m p l o s e ha c r e a d o en Windows . Otras p l a t a f o r m a s pueden # c o m p o r t a r s e de forma d i f e r e n t e , por l a s r a z o n e s d e s c r i t a s m s a b a j o . a >>> f i l e = open ( examples / c h i n e s e . t x t ) >>> a s t r i n g = f i l e . r e a d ( ) Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> F i l e C: \ Python31 \ l i b \ e n c o d i n g s \ cp1252 . py , l i n e 2 3 , in decode return c o d e c s . charmap decode ( input , s e l f . e r r o r s , d e c o d i n g t a b l e ) [ 0 ] UnicodeDecodeError : charmap c o d e c can t decode byte 0 x 8 f i n p o s i t i o n 2 8 : c h a r a c t e r maps t o <u n d e f i n e d > >>>

Qu ha sucedido? No especicamos ninguna codicacin de caracteres, por e o eso Python se ve forzado a utilizar la codicacin de caracteres por defecto. Y cul o a es esta codicacin? Si observas la traza del error observars que est fallando en o a a cp1252.py, lo que signica que Python est utilizando en este caso CP-1252 como la a codicacin de caracteres por defecto. El conjunto de caracteres CP-1252 no soporta o los caracteres que se encuentran en este chero, por eso la lectura falla con un feo UnicodeDecodeError. Pero espera, es peor que eso! La codicacin de caracteres por defecto es dependiente de o la plataforma, por eso este cdigo podr funcioo a nar en tu ordenador (siempre que tu codicacin o de caracteres por defecto fuera UTF-8), pero entonces podr fallar cuando lo distribuyeras a ala guna otra persona (si su codicacin de caracteres o ejemplo CP-1252).

La codicacin de caracteres o por defecto es dependiente de la plataforma. por defecto fuera otra, como por

Si necesitas conocer la codicacin de caracteres por defecto, importa el o mdulo locale y llama a la funcin locale.getpreferredencoding(). En mi o o porttile Windows, devuelve cp1252, pero en mi mquina Linux retorna a a UTF8, lo mismo que en mi MacBookPro. No puedo mantener la consistencia ni en mi propia casa! En cada caso pueden obtenerse diferentes resultados (incluso en Windows) dependiendo de la versin del sistema o operativo instalado y la conguracin del idioma y de los parmetros o a regionales. Por eso es muy importante especicar la codicacin de cao racteres siempre que se abre un chero de texto.

11.2.2.

Objetos de ujo (streams)

Hasta el momento, todo lo que hemos aprendido es que Python dispone de una funcin interna denominada open(). Esta funcin devuelve un objeto de ujo, o o

236

CAP ITULO 11. FICHEROS

que tiene mtodos y atributos para recuperar la informacin y manipular un ujo e o de caracteres.
1 2 3 4 5 6 7 >>> a f i l e = open ( examples / c h i n e s e . t x t , e n c o d i n g= u t f 8 ) >>> a f i l e . name examples / c h i n e s e . t x t >>> a f i l e . e n c o d i n g u t f 8 >>> a f i l e . mode r

1. Lnea 2: El atributo name es el nombre que se pas como parmetro a la o a funcin open() cuando se abri el chero. No est normalizado a un camino o o a absoluto. 2. L nea 4: De igual forma, el atributo encoding reeja la codicacin de caraco teres que se pas como parmetro a la funcin open(). Si no se especica la o a o codicacin de caracteres cuando abriste el chero (mal desarrollador!) eno tonces el atributo reejar la funcin locale.getpreferredencoding(). a o 3. Lnea 6: El atributo mode reeja el modo en el que se abri el chero. Se puede o pasar un parmetro opcional mode a la funcin open(). Si no especicaste el a o modo al abrir el chero Python utiliza por defecto r, lo que signica que se abra solamente para lectura, en modo texto. Como se ver ms tarde en a a este cap tulo, el modo del chero sirve para varios propsitos: escribir, aadir o n o abrir un chero en el modo binario (en el que se trata el contenido como bytes en lugar de cadenas). La documentacin de la funcin open muestra todos los modos vlidos. o o a

11.2.3.

Leer datos de un chero de texto

Despus de abrir un chero para lectura problemente querrs leer su contenido. e a


1 >>> a f i l e = open ( examples / c h i n e s e . t x t , e n c o d i n g= u t f 8 ) 2 >>> a f i l e . r e a d ( ) 3 Dive I n t o Python Python \\n 4 >>> a f i l e . r e a d ( ) 5

1. Lnea 2: Una vez has abierto el chero (con la codicacin de caracteres co o rrecta) para leer de l hay que llamar al mtodo read() del objeto de ujo. El e e resultado es una cadena de texto.

11.2. LEER CONTENIDO DE FICHEROS DE TEXTO

237

2. Linea 4: Aunque pueda ser sorprendente, leer de nuevo del chero no eleva una excepcin. Python non considera que leer ms all del nal del chero sea o a a un error; simplemente retorna una cadena vac a. Especica siempre el parmetro encoding cuando abras un chero. a Cmo habr que hacer si quieres releer el chero? o a
1 2 3 4 5 6 7 8 9 10 11 12 13 # c o n t i n u a c i n d e l e j e m p l o a n t e r i o r o >>> a f i l e . r e a d ( ) >>> a f i l e . s e e k ( 0 ) 0 >>> a f i l e . r e a d ( 1 6 ) Dive I n t o Python >>> a f i l e . r e a d ( 1 ) >>> a f i l e . r e a d ( 1 ) >>> a f i l e . t e l l ( ) 20

1. Lnea 2: Puesto que ests an al nal del chero, ms llamadas al mtodo a u a e read() simplemente retornarn la cadena vac a a. 2. Lnea 4: El mtodo seek() mueve el objeto de ujo a la posicin de un byte e o concreto del chero. 3. Lnea 6: El mtodo read() puede tomar un parmetro opcional, el nmero de e a u caracteres a leer. 4. Lnea 8: Si quieres, puedes leer un carcter cada vez. a 5. Lnea 12: 16 + 1 + 1 = ... 20? Vamos a intentarlo de nuevo.
1 2 3 4 5 6 7 # sigue d e l ejemplo a nt er io r >>> a f i l e . s e e k ( 1 7 ) 17 >>> a f i l e . r e a d ( 1 ) >>> a f i l e . t e l l ( ) 20

1. Lnea 2: Se mueve al byte 17.

238 2. Lnea 4: Lee un carcter. a 3. Lnea 6: Ahora ests en el byte 20. a

CAP ITULO 11. FICHEROS

Lo ves ahora? Los mtodos seek() y tell() siempre cuentan en bytes, pero e como el chero est abierto como texto, el mtodo read() cuenta caracteres. Los a e caracteres chinos necesitan varios bytes para codicarse en UTF8. Los caracteres ingleses unicamente requieren un byte, por eso puedes llegar a pensar que los mtodos e seek() y read() cuentan la misma cosa. Pero esto unicamente es cierto para algunos caracteres. Pero espera, que es peor!
1 >>> a f i l e . s e e k ( 1 8 ) 2 18 3 >>> a f i l e . r e a d ( 1 ) 4 Traceback ( most r e c e n t c a l l l a s t ) : 5 F i l e <p y s h e l l #12> , l i n e 1 , in <module> 6 a f i l e . read (1) 7 F i l e C: \ Python31 \ l i b \ c o d e c s . py , l i n e 3 0 0 , in decode 8 ( r e s u l t , consumed ) = s e l f . b u f f e r d e c o d e ( data , s e l f . e r r o r s , f i n a l ) 9 UnicodeDecodeError : u t f 8 c o d e c can t decode byte 0 x98 i n 10 p o s i t i o n 0 : unexpected code byte

1. Lnea 1: Muvete al byte 18 e intenta leer un carcter. e a 2. Lnea 3: Porqu falla? Porque no existe un carcter en el byte 18. El carcter e a a ms cercano comienza en el byte 17 (y ocupa tres bytes). Intentar leer un a carcter en la mitad dar un error UnicodeDecodeError. a a

11.2.4.

Cerrar cheros

Los cheros abiertos consumen recursos del sistema, y dependiendo del modo de apertura, puede que otros programas no puedan acceder a ellos. Es importante que se cierren los cheros tan pronto como se haya terminado de trabajar con ellos.
1 # sigue d e l ejemplo a nt er io r 2 >>> a f i l e . c l o s e ( )

Bueno eso ha sido algo anticlimtico. a u e El objeto a le1 an existe; el llamar a su mtodo close() no destruye el objeto, pero ste deja de ser util. e
1

que es un objeto de ujo o stream

11.2. LEER CONTENIDO DE FICHEROS DE TEXTO


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # sigue d e l ejemplo a nt er io r >>> a f i l e . r e a d ( ) Traceback ( most r e c e n t c a l l l a s t ) : F i l e <p y s h e l l #24> , l i n e 1 , in <module> a f i l e . read ( ) V a l u e E r r o r : I /O o p e r a t i o n on c l o s e d f i l e . >>> a f i l e . s e e k ( 0 ) Traceback ( most r e c e n t c a l l l a s t ) : F i l e <p y s h e l l #25> , l i n e 1 , in <module> a f i l e . seek (0) V a l u e E r r o r : I /O o p e r a t i o n on c l o s e d f i l e . >>> a f i l e . t e l l ( ) Traceback ( most r e c e n t c a l l l a s t ) : F i l e <p y s h e l l #26> , l i n e 1 , in <module> a f i l e . t e l l () V a l u e E r r o r : I /O o p e r a t i o n on c l o s e d f i l e . >>> a f i l e . c l o s e ( ) >>> a f i l e . c l o s e d True

239

1. Lnea 2: No puedes leer de un chero cerrado; se eleva una excepcin IOError. o 2. Lnea 7: Tampoco puedes moverte en un chero cerrado. 3. Lnea 12: No existe una posicin activa si el chero est cerrado, por eso o a tambin falla el mtodo tell(). e e 4. Lnea 17: Sorprendentemente, tal vez, llamar de nuevo al mtodo close() sobre e un objeto ya cerrado no eleva una excepcin. Simplemente no hace nada. o 5. Lnea 18: Los objetos de ujos cerrados tienen un atributo que s es util: el atributo closed, que sirve para conrmar que el chero est cerrado. a

11.2.5.

Cerrar cheros de forma automtica a

Los objetos de ujo (streams) tiene un mtodo close() para cerrar expl e citamente el ujo. Pero, qu sucede si tu cdigo tiene un error y falla antes de que e o llames al mtodo close()? En teor este chero quedar abierto permanentemente. e a, a Mientras ests depurando un nuevo cdigo en tu ordenador personal, no ser un a o a gran problema. En un servidor de produccin s que puede serlo. o Python 2 ten una solucin para ello: el bloque try...nally. Esta solucin an a o o u funciona en Python 3, y puede encontrarse en el cdigo preexistente o en el otras o personas. Pero Python 2.5 introdujo una solucin ms limpia, que es la preferida en o a Python 3: la sentencia with.

240
1 2 3 4

CAP ITULO 11. FICHEROS

with open ( examples / c h i n e s e . t x t , e n c o d i n g= u t f 8 ) a s a f i l e : a f i l e . seek (17) a c h a r a c t e r = a f i l e . read (1) print ( a c h a r a c t e r )

Este cdigo llama a open() pero no a a le.close(). La sentencia with inicia un o bloque de cdigo, como el de una sentencia if o un bucle for. Dentro del bloque puedes o utilizar la variable a le que contiene el objeto de ujo devuelto por la funcin open(). o Lgicamente, estn disponibles todos los mtodos del objeto de ujo seek(), read() o a e o lo que necesites. Cuando el bloque with naliza, Python llama automticamente a a a le.close(). Lo importante es: no importa cmo o cundo nalices el bloque with, Python o a cerrar el chero... incluso si la salida se produce a causa de una excepcin sin a o manejar. S lo oyes bien, incluso si el cdigo eleva una excepcin y el programa , o o naliza, el chero se cerrar. Garantizado. a En trminos tcnicos, la sentencia with crea un contexto de ejecucin. En e e o estos ejemplo, el objeto de ujo acta como gestor del contexto. Python u a crea el objeto de ujo a le y le dice que est entrando en un contexto de ejecucin. Cuando el bloque with naliza, Python le dice al objeto o de ujo que est saliendo del contexto de ejecucin y el objeto de ujo a o llama por s mismo al mtodo close(). Para conocer ms detalles puedes e a ver el Apndice B, Clases que se pueden utilizar en un bloque with. e No hay nada espec co relacionado con los cheros en una sentencia with, es un marco general para crear contextos de ejecucin y decirle a los objetos que estn o a entrando y saliendo de l. Si el objeto en cuestin es un objeto de ujo (streams), e o entonces hace cosas utiles relacionadas con los cheros (como cerrar el chero au tomticamente). Pero el comportamiento lo dene el propio objeto de ujo, no la a sentencia with. Existen otras muchas formas de utilizar gestores de contexto que no tienen nada que ver con los cheros. Puedes crear los tuyos propios, como vers ms a a tarde neeste mismo cap tulo.

11.2.6.

Leer los datos l nea a l nea

Una l nea en un cheo de texto es lo que te puedes imaginar tecleas unas cuantas palabras y pulsas INTRO y ya ests en una nueva l a nea. Una l nea de texto es una secuencia de caracteres que est delimitada por... qu cosa exactamente? a e Bueno, es complicado de decir, porque los cheros de texto pueden utilizar diferentes caracteres para marcar el nal de una l nea. Cada sistema operativo tiene su propia

11.2. LEER CONTENIDO DE FICHEROS DE TEXTO

241

convencin. En algunos se utiliza el carcter de retorno de carro, otros utilizan el o a caracter de salto de l nea y algunos utilizan ambos al nal de cada l nea. Ahora respira con expresin de relajacin, porque Python controla estos tipos o o diferentes de n de lnea de forma automtica por defecto. Si le dices que quieres a leer un chero de texto de l nea en l nea, Python descubrir por su cuenta la clase a de n de l nea que el chero de texto utiliza y simplemente funcionar como esperas. a Si necesitas un control ms no sobre lo que debe considerar Python a como n de l nea debers pasar el parmetro opcional newline a la funcin a a o open(). Mira la documentacin de la funcin open() para ver los detalles o o necesarios. Bueno, cmo hay que hacer para leer una l o nea cada vez? Es simple, es bello.
1 2 3 4 5 line number = 0 with open ( examples / f a v o r i t e p e o p l e . t x t , e n c o d i n g= u t f 8 ) a s a f i l e : f o r a l i n e in a f i l e : l i n e n u m b e r += 1 print ( {: >4} {} . format ( l i n e n u m b e r , a l i n e . r s t r i p ( ) ) )

1. Lnea 2: Utilizando el patrn with abres el chero de forma segura y dejas a o Python que lo cierre por ti. 2. Lnea 3: Para leer un chero l nea a l nea lo mejor es utilizar el bucle for. Adems de tener mtodos expl a e citos como el read(), un objeto de ujoo tambin e es un iterador que retorna una l nea del chero cada vez que le pides un valor. 3. Lnea 5: Si utilizas el mtodo format puedes ir imprimendo el nmero de l e u nea y la propia l nea. El especicador de formaot {:4} signica que imprima el parmetro justicado a la derecha dentro de cuatro espacios. La variable a line a contiene una l nea completa, incluyendo el retorno de carro. El mtodo rstrip() e de las cadenas de texto elimina los espacios en blanco del nal de una cadena, incluyendo los caracteres de retorno de carro y salto de l nea.

242
1 2 3 4 5 6 7 8 9 10 11

CAP ITULO 11. FICHEROS

y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 $ python3 examples / o n e l i n e . py 1 Dora 2 Ethan 3 Wesley 4 John 5 Anne 6 Mike 7 Chris 8 Sarah 9 Alex 10 L i z z i e

Fall con este error? o


1 2 3 4 5 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 $ python3 examples / o n e l i n e . py Traceback ( most r e c e n t c a l l l a s t ) : F i l e examples / o n e l i n e . py , l i n e 4 , in <module> print ( {: >4} {} . format ( l i n e n u m b e r , a l i n e . r s t r i p ( ) ) ) V a l u e E r r o r : z e r o l e n g t h f i e l d name in format

Si fue as es que probablemente ests utilizando Python 3.0. Deber actuali e as zarte a Python 3.1. Python 3.0 soportaba el formateo de cadenas, pero unicamente con especica dores de formato numerados expl citamente. Python 3.1 te permite omitir el ndice del parmetro en los especicadores de formato. Esta ser la versin compatible en a a o Python 3.0, para que puedas compararla con la anterior.
1 print ( {0: >4} {1} . format ( l i n e n u m b e r , a l i n e . r s t r i p ( ) ) )

11.3.

Escribir en cheros de texto

Se puede escribir en cheros de forma parecida a como se lee de ellos. Primero se abre el chero y se obtiene el objeto de ujo, luego se utilizan los mtodos del e objeto de ujo que sirven para escribir datos en el chero, para terminar se debe cerrar el chero. Para abrir un chero para escribir se debe utilizar la funcin open() y especicar el modo o de escritura. Existen dos modos de escritura: Modo de escritura que sobreescribe el chero. Se debe pasar el parmetro moa de=w a la funcin open(). o

Simplemente abre el chero y comienza a escribir.

11.3. ESCRIBIR EN FICHEROS DE TEXTO Modo de anexacin que aade datos al o n nal del chero, conservando los datos que existieran anteriormente. Se debe pasar el parmetro mode=a a la funcin open(). a o

243

Cualquiera de los dos modos crear el chero automticamente si no existiera a a ya, por lo que no hay necesidad de ningn tipo de cdigo que compruebe si el u o chero an no existe, crea un nuevo chero vac para que lo pueda abrir despus u o e por primera vez. Simplemente abre el chero y comienza a escribir. Siempre deber cerrar el chero cuando hayas terminado de escribir con el n as de liberar al manejador del chero y asegurar que los datos realmente se han escrito en el disco. Como cuando se leen datos de un chero, se puede utilizar el mtodo e close() o puedes utilizar la sentencia with y dejar a Python que cierre el chero por ti. Apuesto a que puedes adivinar la tcnica que te recomiendo. e
1 2 3 4 5 6 7 8 9 10 >>> with open ( t e s t . l o g , mode= w , e n c o d i n g= u t f 8 ) a s a f i l e : ... a f i l e . write ( t e s t succeeded ) >>> with open ( t e s t . l o g , e n c o d i n g= u t f 8 ) a s a f i l e : ... print ( a f i l e . r e a d ( ) ) t e s t succeeded >>> with open ( t e s t . l o g , mode= a , e n c o d i n g= u t f 8 ) a s a f i l e : ... a f i l e . w r i t e ( and a g a i n ) >>> with open ( t e s t . l o g , e n c o d i n g= u t f 8 ) a s a f i l e : ... print ( a f i l e . r e a d ( ) ) t e s t succeededand again

1. Lnea 1: Comienzas creando un nuevo chero test.log (o sobreescribiendo el chero existente) y abriendo el chero en modo escritura. El parmetro moa de=w signica que abres el chero para escribir en l. Si, tan peligroso como e suena. Espero que no te importase el contenido previo que el chero pudiera tener (si ten alguno), porque todos esos datos ya han desaparecido. a 2. Lnea 2: Puedes aadir datos al chero recin abierto utilizando el mtodo n e e write() del objeto de ujo devuelto por la funcin open(). Despus el bloque o e with termina y Python cierra automticamente el chero. a 3. Lnea 6: Estuvo bien, vamos a hacerlo de nuevo. Pero esta vez con mode=a pa ra aadir al nal del chero en lugar de sobreescribirlo. Aadir nunca daar el n n n a contenido preexistente del chero. 4. Lnea 10: Ahora el chero contiene tanto la l nea original como la segunda l nea que aadiste a test.log. Ten en cuenta que no se incluyen retornos de n caro. Puesto que no los incluiste expl citamente al chero ninguna de las veces,

244

CAP ITULO 11. FICHEROS el chero no los contiene. Puedes escribir un retorno de carro utilizando el carcter a n. Al no haberlo incluido, todo lo que escribiste al chero acaba en una unica l nea.

11.3.1.

Codicacin de caracteres, otra vez o

Observaste el parmetro encoding que se pasaba a la funcin open() mientras a o abr el chero para escritura? Es importante, nunca lo omitas! Como has visto as al comienzo de este cap tulo, los cheros no contienen cadenas de texto, contienen bytes. La lectura de una cadena de texto de un chero de texto funciona porque le dices a Python la codicacin de caracteres que tiene que utilizar para leer el ujo o de bytes y convertirla a una cadena de texto. Al escribir texto a un chero ocurre el mismo problema pero en sentido inverso. No puedes escribir caracteres a un chero; los caracteres son una abstraccin. Para escribir a un chero, Python necesita saber o cmo convertir la cadena de texto en una secuencia de bytes. La unica forma de o estar seguro de ejecutar la conversin correcta es especicar el parmetro encoding o a cuando abres el chero en modo escritura.

11.4.

Ficheros binarios

No todos los cheros contienen texto. Algunos contienen fotos de mi perro.

1 2 3 4 5 6 7 8 9

>>> an image = open ( examples / b e a u r e g a r d . j p g , mode= rb ) >>> an image . mode rb >>> an image . name examples / b e a u r e g a r d . j p g >>> an image . e n c o d i n g Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> A t t r i b u t e E r r o r : i o . B u f f e r e d R e a d e r o b j e c t has no a t t r i b u t e e n c o d i n g

11.4. FICHEROS BINARIOS

245

1. Lnea 1: Abrir un chero en modo binario es simple pero sutil. La unica dife rencia respecto de abrirlo en modo de texto es que el parmetro mode contiene a un carcter b adicional. a 2. Lnea 2: El objeto de ujo que obtienes cuando se abre un chero en modo binario tiene muchos de los atributos que ya se han visto, incluido mode, que reeja el parmetro mode que se pas a la funcin open(). a o o 3. Lnea 4: Los objetos de ujo binarios tambin tienen el atributo name, como e pasa con los objetos de ujo de texto. 4. Lnea 6: Aqu hay una diferencia: un objeto de ujo binario no tiene el atributo encoding. Tiene sentido verdad? Ests leyendo (o escribiendo) bytes, no cadea nas de texto, por lo que no hay ninguna conversin que hacer. Lo que obtienes o de un chero binario es exactamente lo que pones en l, no hay necesidad de e ninguna conversin. o He mencionado que estabas leyendo bytes? Oh! pues ests leyendo bytes. a
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # sigue d e l ejemplo a nt er io r >>> an image . t e l l ( ) 0 >>> data = an image . r e a d ( 3 ) >>> data b \ x f f \ xd8 \ x f f >>> type ( data ) <c l a s s b y t e s > >>> an image . t e l l ( ) 3 >>> an image . s e e k ( 0 ) 0 >>> data = an image . r e a d ( ) >>> l e n ( data ) 3150

1. Lnea 4: Como con los cheros de texto, los cheros binarios se pueden leer poco a poco. Pero hay una diferencia crucial... 2. Lnea 7: ...ests leyendo bytes, no cadenas de texto. Puesto que el chero se a ha abierto en modo binario, el mtodo read() toma como parmetro el nmero e a u de bytes que se desea leer, no el nmero de caracteres. u 3. Lnea 9: Lo que signic que nunca hay diferencia entre el nmero que le pasas u como parmetro al mtodo read(9 y el a e ndice que devuelve el mtodo tell(). e El mtodo read() lee bytes, y los mtodos seek() y tell() cuentan el nmero de e e u bytes le dos. Siempre coinciden en el caso de los cheros binarios.

246

CAP ITULO 11. FICHEROS

11.5.

Objetos de ujo obtenidos de fuentes que no son cheros

Imagina que ests escribiendo una librer y una de las funciones de sta lee a a, e algunos datos de un chero. La funcin podr tomar como parmetro el nombre o a a del chero en formato cadena de texto, abrir el chero para lectura, leer de l a a e y lo cerrar antes de terminar. Por no deber hacer esto. En vez de esto, tu API a as deber tomar como parmetro un objeto de ujo cualquiera. a a En el caso ms simple, un objeto de ujo a es cualquier objeto que tenga un mtodo read() e Para leer de un chero cticio, con un parmetro opcional size para pasarle el a simplemente utiliza read(). tamao a leer y que devuelve una cadena de texn to. Cuando se le llama sin el parmetro size, el a mtod read() deber leer todo lo que falta por leer para devolver todos los datos e a como una unica cadena de texto. Cuando se llama con el parmetro size, lee esa a cantidad desde la entrada devolviendo una cadena de texto con estos datos. Cuando se le llama de nuevo, contina por donde qued y devuelve el siguiente trozo de los u o datos de entrada. Este comportamiento es idntico a los objetos de ujo que obtienes cuando e abres un chero real. La diferencia es que no te ests limitando a cheros reales. La a fuente de etrada que se est leyendo puede ser cualquier cosa: una pgina web, una a a cadena en memoria o, incluso, la salida de otro programa. Siempre que tus funciones tomen como parmetro un objeto de ujo y llamen al mtodo read() podrs manejar a e a cualquier fuente de entrada que se comporte como un chero, sin que tengas que escribir cdigo que maneje cada tipo espec o co de entrada.

11.5. OBJETOS DE FLUJO OBTENIDOS DE FUENTES QUE NO SON FICHEROS247


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 >>> a s t r i n g = PapayaWhip i s t h e new b l a c k . >>> import i o >>> a f i l e = i o . S t r i n g I O ( a s t r i n g ) >>> a f i l e . r e a d ( ) PapayaWhip i s t h e new b l a c k . >>> a f i l e . r e a d ( ) >>> a f i l e . s e e k ( 0 ) 0 >>> a f i l e . r e a d ( 1 0 ) PapayaWhip >>> a f i l e . t e l l ( ) 10 >>> a f i l e . s e e k ( 1 8 ) 18 >>> a f i l e . r e a d ( ) new b l a c k .

1. Lnea 2: El mdulo io dene la clase StringIO que puedes utilizar para tratar o a las cadenas de texto en memoria como si fuesen un chero. 2. Lnea 3: Para crear un objeto de ujo a partir de una cadena de texto debes crear una instancia de la clase io.StringIO() y pasarle la cadena de texto que quieres recorrer como si fuesen datos de un chero. Ahora tienes un objeto de ujo y puedes hacer todo lo que puedes hacer con los objetos de ujo. 3. Lnea 4: Al llamar al mtodo read() se lee el chero completo, lo que en e el caso de un objeto StringIO es tan simple como obtener la cadena de texto original. 4. Lnea 6: Como pasa con un chero real, el llamar de nuevo a read() devuelve una cadena vac a. 5. Lnea 8: Puedes buscar expl citamente el comienzo de la cadena, como en un chero real, mediante el uso del mtodo seek() del objeto StringIO. e 6. Lnea 10: Tambin puedes leer la cadena a trozos pasndole el parmetro size e a a al mtodo read(). e io.StringIO te permite manipular una cadena de texto como si fuese un chero de texto. Tambin existe una clase io.BytesIO que te permite e tratar un array de bytes como un chero binario.

248

CAP ITULO 11. FICHEROS

11.5.1.

Manipular cheros comprimidos

La librer estndar de Python contiene mdulos que permiten leer y escribir a a o cheros comprimidos. Existen diversos sistemas de compresin; los dos ms populao a res en sistemas no windows son gzip y bzip2 (Tambin te puedes encontrar archivos e PKZIP y GNU Tar. Python tambin dispone de mdulos para ellos). e o El mdulo gzip te permite crear un objeto de ujo para leer y escribir cheros o comprimidos con el formato gzip. Este objeto dispone del mtodo read() (si lo abriste e para lectura) y del mtodo write() (si lo abriste de escritura). Esto signica que e puedes utilizar los mtodos que ya has aprendido para leer o escribir directamente e cheros comprimidos en formato gzip sin tener que crear un chero temporal con los datos sin comprimir. Como un bonus aadido, tambin permite el uso de la sentencia with por lo n e que puedes dejar a Python que cierre el chero comprimido de forma automtica a cuando hayas terminado de trabajar con l. e
1 2 3 4 5 6 7 8 9 10 11 12 13 14 y o u @ l o c a l h o s t : $ python3 >>> import g z i p >>> with g z i p . open ( out . l o g . gz , mode= wb ) a s z f i l e : ... z f i l e . write ( ... A n i n e m i l e walk i s no j o k e , e s p e c i a l l y i n t h e r a i n . . encode ( ... u t f 8 ) ) ... >>> e x i t ( )

rwr r

y o u @ l o c a l h o s t : $ l s l out . l o g . gz 1 mark mark 79 2009 07 19 1 4 : 2 9 out . l o g . gz y o u @ l o c a l h o s t : $ g u n z i p out . l o g . gz y o u @ l o c a l h o s t : $ c a t out . l o g A n i n e m i l e walk i s no j o k e , e s p e c i a l l y in t h e r a i n .

1. Lnea 3: Los cheros comprimidos se deben abrir siempre en modo binario (Observa el carcter b en el parmetro mode). a a 2. Lnea 8: Este ejemplo lo constru en Linux. Si no ests familiarizado con la a l nea de comando, este comando te muestra un listado largo del chero comprimido que acabas de crear con la consola de Python. 3. Lnea 10: El comando gunzip descomprime el chero y almacena el contenido en un nuevo chero con el mismo nombre que el original pero sin la extensin o .gz. 4. Lnea 11: El comando cat muestra el contenido de un chero. Este chero con

11.6. FLUJOS DE ENTRADA, SALIDA Y ERROR ESTANDARES

249

tiene la cadena de texto que escribiste directamente en el chero comprimido out.log.gz desde la consola de Python. Te pas este error? o
1 >>> with g z i p . open ( out . l o g . gz , mode= wb ) a s z f i l e : 2 ... z f i l e . write ( 3 ... A n i n e m i l e walk i s no j o k e , e s p e c i a l l y i n t h e r a i n . . encode ( 4 ... u t f 8 ) ) 5 ... 6 Traceback ( most r e c e n t c a l l l a s t ) : 7 F i l e <s t d i n > , l i n e 1 , in <module> 8 A t t r i b u t e E r r o r : G z i p F i l e o b j e c t has no a t t r i b u t e e x i t

Si fue as posiblemente ests utilizando Python 3.0. Deber actualizarte a , e as Python 3.1. Python 3.0 ten un mdulo gzip pero no soportaba el uso de los objea o tos de ujo de cheros comprimidos como parte de un gestor de contexto. Python 3.1 aade esta funcionalidad que permite utilizar estos objetos como parte de la n sentencia with.

11.6.

Flujos de entrada, salida y error estndares a

Los gurs de la l u nea de comando estn familiarizados con el concepto de a entrada estndar, salida estndar y error estndar. Esta seccin es para el resto de a a a o vosotros. La salida estndar y la salida de error a estndar (comunmente abreviadas como stdout a sys.stdin, sys.stdout, sys.stderr. y stderr) son tuber as (pipes) que vienen en cualquier sistema de tipo UNIX, incluyendo Mac OS X y Linux. Cuando llamas a la funcin print(), lo que se imprime se env a la o a tuber de salida stdout. Cuando tu programa falla e imprime la traza de error, la a salida va a la tuber stderr. Por defecto, ambas tuber estn conectadas directaa as a menten a la ventana del terminal en el que ests trabajando; cuando tu programa a imprime algo, ves la salida en la ventana del terminal; y cuando el programa falla, tambin ves la traza de error en la misma ventana del terminal. En la consola grca e a de Python, las tuber stdout y stderr estn conectadas por defecto a la ventana as a interactiva en la que te encuentras.

250
1 2 3 4 5 6 7 8 9 10 11 12 >>> f o r i in r a n g e ( 3 ) : ... print ( PapayaWhip ) PapayaWhip PapayaWhip PapayaWhip >>> import s y s >>> f o r i in r a n g e ( 3 ) : . . . sys . stdout . write ( i s the ) i s t h e i s t h e i s the >>> f o r i in r a n g e ( 3 ) : . . . s y s . s t d e r r . w r i t e ( new b l a c k ) new blacknew blacknew b l a c k

CAP ITULO 11. FICHEROS

1. Lnea 2: La funcin print() en un bucle. Nada sorprendente en este trozo de o cdigo. o 2. Lnea 8: stdout est denida en el mdulo sys y es un objeto de ujo. Si se a o llama a su funcin write() se imprimir la cadena de texto que le pases como o a parmetro. De hecho, es lo que hace la funcin print: aade un retorno de carro a o n a la cadena que quieres imprimir y llama a sys.stdout.write. 3. Lnea 11: En el caso ms simple, sys.stdout y sys.stderr env su salida al a an mismo sitio: en entorno integrado de desarrollo de Python (si te encuentras en uno) o el terminal (si ests ejecutando Python desde la l a nea de comando). Como en el caso de la salida estndar, la salida de error tampoco aade retornos a n de carro por ti. Si quieres retornos de carro, tienes que aadirlos. n sys.stdout y sys.stderr son objetos de ujo, abiertos como de escritura unica mente. Si se intenta llamar a sus mtodos read() se elevar el error IOError. e a
1 >>> import s y s 2 >>> s y s . s t d o u t . r e a d ( ) 3 Traceback ( most r e c e n t c a l l l a s t ) : 4 F i l e <s t d i n > , l i n e 1 , in <module> 5 IOError : not r e a d a b l e

11.6.1.

Redireccin de la salida estndar o a

sys.stdout y sys.stderr son objetos de ujo, aunque unicamente soportan la escritura. Pero no son constantes, son variables. Esto signica que puedes asignarles un nuevo valor cualquier otro objeto de ujo para redirigir su salida.

11.6. FLUJOS DE ENTRADA, SALIDA Y ERROR ESTANDARES


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import s y s class RedirectStdoutTo : def i n i t ( s e l f , out new ) : s e l f . out new = out new def enter ( self ): s e l f . out old = sys . stdout s y s . s t d o u t = s e l f . out new e x i t ( s e l f , args ) : sys . stdout = s e l f . out old

251

def

print ( A ) with open ( out . l o g , mode= w , e n c o d i n g= u t f 8 ) \ as a f i l e , RedirectStdoutTo ( a f i l e ) : print ( B ) print ( C )

Prueba esto:
1 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 s t d o u t . py 2 A 3 C 4 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ c a t out . l o g 5 B

Te pas este error? o


1 2 3 4 5 6 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ python3 s t d o u t . py F i l e s t d o u t . py , l i n e 15 with open ( out . l o g , mode= w , e n c o d i n g= u t f 8 ) \ as a f i l e , RedirectStdoutTo ( a f i l e ) : SyntaxE rror : i n v a l i d s y n t a x

Si es as probablemente ests utilizando Python 3.0. Posiblemente deber , e as actualizarte a Python 3.1. Python 3.0 soporta la sentencia with, pero cada sentencia puede utilizar uni camente un gestor de contexto. Python 3.1 te permite encadenar varios gestores de contexto en una unica sentencia with. Vamos a ver la ultima parte primero.

252
1 2 3 4 5

CAP ITULO 11. FICHEROS

print ( A ) with open ( out . l o g , mode= w , e n c o d i n g= u t f 8 ) \ as a f i l e , RedirectStdoutTo ( a f i l e ) : print ( B ) print ( C )

Se trata de una sentencia with compleja. Djame que la reescriba como algo e ms reconocible. a
1 2 3 with open ( out . l o g , mode= w , e n c o d i n g= u t f 8 ) a s a f i l e : with R e d i r e c t S t d o u t T o ( a f i l e ) : print ( B )

Como muestra esta reescritura, en realidad se trata de dos sentencias with, una de ellas anidada en el mbito de la otra. La sentencia with exterior deber ya a a serte familiar: abre un chero de texto codicado en UTF8 denominado out.log, para escritura; y lo asigna a la variable denominada a le. Pero no es lo unico extrao n aqu .
1 with R e d i r e c t S t d o u t T o ( a f i l e )

Dnde est la clasula as aqu La sentencia with no la requiere. Al igual que o a u ? puedes llamar a una funcin e ignorar su valor de retorno, puedes crear una sentencia o with sin asignar el contexto resultante a una variable. En este caso solamente ests a interesado en el efecto lateral del contexto RedirectStdoutTo. Cul es el efecto lateral de este contexto? Echa un vistazo a la clase Redireca tStdoutTo. Esta clase es un gestor de contexto a medida. Cualquier clase puede serlo si dene dos mtodos especiales: enter () y exit (). e
1 2 3 4 5 6 7 8 9 10 class RedirectStdoutTo : def i n i t ( s e l f , out new ) : s e l f . out new = out new def enter ( self ): s e l f . out old = sys . stdout s y s . s t d o u t = s e l f . out new e x i t ( s e l f , args ) : sys . stdout = s e l f . out old

def

1. Lnea 2: El mtodo init () se llama inmediatamente despus de que la ins e e tancia se crea. Toma un parmetro, el objeto de ujo que quieres utilizar como a salida estndar durante la vida del contexto. Este mtodo simplemente almaa e cena el objeto de ujo en una variable para que los otros mtodos lo puedan e usar ms tarde. a

11.6. FLUJOS DE ENTRADA, SALIDA Y ERROR ESTANDARES

253

2. Lnea 5: El mtodo enter () es un mtodo especial de la clase; Python lo e e llama cuando se entra en un contexto (por ejemplo: al comienzo de una bloque with). Este mtodo almacena el valor actual de sys.stdout en self.out old, luego e redirige la salida estndar asignando self.out new a sys.stdout. a 3. Lnea 9: El mtodo exit () es otro mtodo especial de la clase; Python lo e e llama cuando sale del contexto (por ejemplo: al nal del bloque with). Este mtodo restablee la salida estandar a su valor original asignando el valor e almacenado self.old value a sys.stdout. Al juntarlo todo:
1 2 3 4 5 print ( A ) with open ( out . l o g , mode= w , e n c o d i n g= u t f 8 ) a s a f i l e , \ RedirectStdoutTo ( a f i l e ) : print ( B ) print ( C )

1. Lnea 1: Imprimir en la ventana interativa (del entorno integrado de desarro a llo o el terminal, dependiendo de cmo ests ejecutando este programa). o e 2. Lnea 2: La sentencia with recibe una lista de contextos separada por comas. Esta lista acta como una serie de bloques with anidados. El primer contexto es u el bloque externo, el ultimo es el interno. El primer contexto abre un chero, el segundo redirige sys.stdout al objeto de ujo que se cre en el primer contexto. o 3. Lnea 3: Debido a que la funcin print() se ejecut en el contexto creado por la o o sentencia with, no se imprimir a la pantalla, se imprimir en el chero out.log. a a 4. Lnea 4: El bloque with se ha acabado. Python le ha dicho a cada gestor de contexto que hagan lo que tengan que hacer para salir del contexto. Los gestores de contexto forman una pila LIFO (Last-in-rst-out: Primero en entrar, ultimo en salir). En la salida, el segundo contexto vuelve a poner el valor origi nal de sys.stdout, luego el primer contexto cierra el chero out.log. Puesto que la salida estndar ha vuelto a su valor inicial, la llamada a la funcin print() a o de nuevo imprimir en la pantalla. a La redireccin de la salida de error estndar funciona de igual manera, simpleo a mente cambiando sys.stdout por sys.stderr.

254

CAP ITULO 11. FICHEROS

11.7.

Lecturas recomendadas

Lectura y escritura de cheros en el tutorial de Python: http://docs.python.org/tutorial/inputoutput.html#reading-and-writing-les El mdulo io: o http://docs.python.org/3.1/library/io.html Objetos de ujo (streams): http://docs.python.org/3.1/library/stdtypes.html#le-objects Tipos de gestores de contexto: http://docs.python.org/3.1/library/stdtypes.html#context-manager-types sys.stdout y sys.stderr: http://docs.python.org/3.1/library/sys.html#sys.stdout FUSE en la Wikipedia: http://en.wikipedia.org/wiki/Filesystem in Userspace

Cap tulo 12 XML


Nivel de dicultad:  En el gobierno de Aristemo, Draco aplic sus ordenanzas. o Aristteles o

12.1.

Inmersin o

La mayor de los cap a tulos de este libro se han desarrollado alrededor de un cdigo de ejemplo. Pero XML no trata sobre cdigo, trata sobre datos. Un uso muy o o comn para XML es la provisin de contenidos sindicados que lista los ultimos u o art culos de un blog, foro u otro sitio web con actualizaciones frecuentes. El software para blogs ms popular puede generar fuentes de informacin y actualizarlas cada vez a o que hay nuevos art culos, hilos de discusin o nuevas entradas en un blog. Puedes o seguir un blog sscribindote a su canal (feed), y puedes seguir diversos blogs e mediante un agregador de canales como el lector de Google. Aqu estn los datos de XML que utilizaremos en este cap a tulo. Es un canal espec camente, una fuente de informacin sindicada Atom. o

255

256

CAP ITULO 12. XML

1 <?xml v e r s i o n= 1 . 0 e n c o d i n g= u t f 8 ?> 2 <f e e d xmlns= h t t p : / /www. w3 . o r g /2005/Atom xml : l a n g= en > 3 < t i t l e >d i v e i n t o mark</ t i t l e > 4 <s u b t i t l e >c u r r e n t l y between a d d i c t i o n s </ s u b t i t l e > 5 <id>t a g : d i v e i n t o m a r k . org ,2001 07 29:/ </ id> 6 <updated >2009 03 27T21 : 5 6 : 0 7 Z</updated> 7 <l i n k r e l= a l t e r n a t e type= t e x t / html 8 h r e f= h t t p : / / d i v e i n t o m a r k . o r g / /> 9 <l i n k r e l= s e l f type= a p p l i c a t i o n /atom+xml 10 h r e f= h t t p : / / d i v e i n t o m a r k . o r g / f e e d / /> 11 <entry > 12 <author> 13 <name>Mark</name> 14 <u r i >h t t p : / / d i v e i n t o m a r k . o r g /</ u r i > 15 </author> 16 < t i t l e >Dive i n t o h i s t o r y , 2009 e d i t i o n </ t i t l e > 17 <l i n k r e l= a l t e r n a t e type= t e x t / html 18 h r e f= h t t p : / / d i v e i n t o m a r k . o r g / a r c h i v e s /2009/03/27/ ( s i g u e a b a j o ) 19 dive i n t o h i s t o r y 2009 e d i t i o n /> 20 <id>t a g : d i v e i n t o m a r k . org ,2009 03 27:/ a r c h i v e s /20090327172042 </ id> 21 <updated >2009 03 27T21 : 5 6 : 0 7 Z</updated> 22 <p u b l i s h e d >2009 03 27T17 : 2 0 : 4 2 Z</p u b l i s h e d > 23 <c a t e g o r y scheme= h t t p : / / d i v e i n t o m a r k . o r g term= d i v e i n t o p y t h o n /> 24 <c a t e g o r y scheme= h t t p : / / d i v e i n t o m a r k . o r g term= docbook /> 25 <c a t e g o r y scheme= h t t p : / / d i v e i n t o m a r k . o r g term= html /> 26 <summary type= html >P u t t i n g an e n t i r e c h a p t e r on one page sounds 27 b l o a t e d , but c o n s i d e r t h i s &amp ; mdash ; my l o n g e s t c h a p t e r s o f a r 28 would be 75 p r i n t e d pages , and i t l o a d s in under 5 29 s e c o n d s&amp ; h e l l i p ; On d i a l u p .</summary> 30 </entry >

12.2. CURSO RAPIDO DE 5 MINUTOS SOBRE XML

257

1 <entry > 2 <author> 3 <name>Mark</name> 4 <u r i >h t t p : / / d i v e i n t o m a r k . o r g /</ u r i > 5 </author> 6 < t i t l e >A c c e s s i b i l i t y i s a h a r s h m i s t r e s s </ t i t l e > 7 <l i n k r e l= a l t e r n a t e type= t e x t / html 8 h r e f= h t t p : / / d i v e i n t o m a r k . o r g / a r c h i v e s /2009/03/21/ ( s i g u e ) 9 a c c e s s i b i l i t y i s aharsh m i s t r e s s /> 10 <id>t a g : d i v e i n t o m a r k . org ,2009 03 21:/ a r c h i v e s /20090321200928 </ id> 11 <updated >2009 03 22T01 : 0 5 : 3 7 Z</updated> 12 <p u b l i s h e d >2009 03 21T20 : 0 9 : 2 8 Z</p u b l i s h e d > 13 <c a t e g o r y scheme= h t t p : / / d i v e i n t o m a r k . o r g term= a c c e s s i b i l i t y /> 14 <summary type= html >The a c c e s s i b i l i t y orthodoxy d o e s not p e r m i t 15 people to question the value of f e a t u r e s that are r a r e l y 16 u s e f u l and r a r e l y used .</summary> 17 </entry > 18 <entry > 19 <author> 20 <name>Mark</name> 21 </author> 22 < t i t l e >A g e n t l e i n t r o d u c t i o n t o v i d e o encoding , p a r t 1 : 23 c o n t a i n e r formats </ t i t l e > 24 <l i n k r e l= a l t e r n a t e type= t e x t / html 25 h r e f= h t t p : / / d i v e i n t o m a r k . o r g / a r c h i v e s /2008/12/18/ ( s i g u e ) 26 g i v e part 1 c o n t a i n e r f o r m a t s /> 27 <id>t a g : d i v e i n t o m a r k . org ,2008 12 18:/ a r c h i v e s /20081218155422 </ id> 28 <updated >2009 01 11T19 : 3 9 : 2 2 Z</updated> 29 <p u b l i s h e d >2008 12 18T15 : 5 4 : 2 2 Z</p u b l i s h e d > 30 <c a t e g o r y scheme= h t t p : / / d i v e i n t o m a r k . o r g term= a s f /> 31 <c a t e g o r y scheme= h t t p : / / d i v e i n t o m a r k . o r g term= a v i /> 32 <c a t e g o r y scheme= h t t p : / / d i v e i n t o m a r k . o r g term= e n c o d i n g /> 33 <c a t e g o r y scheme= h t t p : / / d i v e i n t o m a r k . o r g term= f l v /> 34 <c a t e g o r y scheme= h t t p : / / d i v e i n t o m a r k . o r g term= GIVE /> 35 <c a t e g o r y scheme= h t t p : / / d i v e i n t o m a r k . o r g term= mp4 /> 36 <c a t e g o r y scheme= h t t p : / / d i v e i n t o m a r k . o r g term= ogg /> 37 <c a t e g o r y scheme= h t t p : / / d i v e i n t o m a r k . o r g term= v i d e o /> 38 <summary type= html >These n o t e s w i l l e v e n t u a l l y become p a r t o f a 39 t e c h t a l k on v i d e o e n c o d i n g .</summary> 40 </entry > 41 </f e e d >

12.2.

Curso rpido de 5 minutos sobre XML a

Si conoces ya XML puedes saltarte esta seccin. o XML es una forma generalizada de describir una estructura de datos jerrquica. a

258

CAP ITULO 12. XML

Un documento XML contiene uno o ms elementos, que estn delimitados por etia a quetas de inicio y n. Lo siguiente es un documento XML completo (aunque bastante aburrido).
1 <foo > 2 </foo >

1. Lnea 1: Esta es la etiqueta de inicio del elemento foo. 2. Lnea 2: Esta es la etiqueta de n del elemento foo, que es pareja de la anterior. Como los parntesis en la escritura, matemticas o cdigo, toda etiqueta de e a o inicio debe cerrase con una etiqueta de n. Los elementos se pueden anidar a cualquier profundidad. Si un elemento bar se encuentra dentro de un elemento foo, se dice que bar es un subelemento o hijo de foo.
1 <foo > 2 <bar></bar> 3 </foo >

Al primer elemento de un documento XML se le llama el elemento raz. Un documento XML unicamente puede tener un elemento ra Lo siguiente no es un z. documento XML porque tiene dos elementos ra z:
1 <foo ></foo > 2 <bar></bar>

Los elementos pueden tener atributos, que son parejas de nombres con valores. Los atributos se deben incluir dentro de la etiqueta de inicio del elemento y deben estar separados por un espacio en blanco. Los nombres de atributo no se pueden repetir dentro de un elemento. Los valores de los atributos deben ir entre comillas. Es posible utilizar tanto comillas simples como dobles.
1 <f o o l a n g= en > 2 <bar i d= papayawhip l a n g= f r ></bar> 3 </foo >

1. Lnea 1: El elemento foo tiene un atributo denominado lang. El valor del atributo lang es en. 2. Lnea 2: El elemento bar tiene dos atributos. El valor del atributo lang es fr. Esto no entra en conicto con el elemento foo, cada elemento tiene su propio conjunto de atributos.

12.2. CURSO RAPIDO DE 5 MINUTOS SOBRE XML

259

Si un elemento tiene ms de un atributo, el orden de los mismos no es signia cativo. Los atributos de un elemento forman un conjunto desordenado de claves y valores, como en un diccionario de Python. No exite l mite en el nmero de atributos u que puedes denir para cada elemento. Los elementos pueden contener texto.
1 <f o o l a n g= en > 2 <bar l a n g= f r >PapayaWhip</bar> 3 </foo >

Existe una forma de escribir elementos vac de forma compacta. Colocando os un carcter / al nal de la etiqueta de inicio se puede evitar tener que escribir la a etiqueta de n. El documento XML del ejemplo anterior se puede escribir de esta otra forma:
1 <f o o />

Como pasa con las funciones de Python que se pueden declarar en diferentes mdulos, los elementos XML se pueden declarar en diferentes espacios de nombre. o Los espacios de nombre se suelen representar como URLs. Se puede utilizar una declaracin xmlns para denir un espacio de nombres por defecto. Una declaracin o o de un espacio de nombres es parecida a un atributo, pero tiene un signicado y propsito diferente. o
1 <f e e d xmlns= h t t p : / /www. w3 . o r g /2005/Atom > 2 < t i t l e >d i v e i n t o mark</ t i t l e > 3 </f e e d >

1. Lnea 1: El elemento feed se encuentra en el espacio de nombres http://www.w3.org/2005/Atom 2. Lnea 2: El elemento title se encuentra tambin en el espacio de nombres e http://www.w3.org/2005/Atom. La declaracin del espacio de nombres afecta o al elemento en el que est declarado y a todos los elementos hijo. a
1 <atom : f e e d xmlns : atom= h t t p : / /www. w3 . o r g /2005/Atom > 2 <atom : t i t l e >d i v e i n t o mark</atom : t i t l e > 3 </atom : f e e d >

1. Lnea 1: El elemento feed se encuentra en el espacio de nombres http://www.w3.org/2005/Atom 2. Lnea 2: El elemento title tambin se encuentra en el espacio de nombres e http://www.w3.org/2005/Atom.

260

CAP ITULO 12. XML

En lo que concierne al analizador de XML, los dos documentos anteriores son idnticos. Espacio de nombres + nombre de elemento = identidad en XML. Los pree jos existen unicamente para referirse a los espacios de nombres, por lo que el prejo utilizado en la prctica (atom:) es irrelevante. Los espacios de nombre coinciden, a los nombres de elemento coinciden, los atributos (o falta de ellos) coinciden y cada contenido de texto coincide, por lo que estos dos documentos XML son el idnticos e a efectos prcticos. a Finalmente, los documentos XML pueden contener en la primera l nea informacin sobre la codicacin de caracteres, antes del elemento ra Si tienes curiosidad o o z. sobre cmo un documento puede contener informacin que necesita conocerse antes o o de que el documento pueda analizarse consulta la Seccin F de la especicacin XML o o (http://www.w3.org/TR/REC-xml/#sec-guessing-no-ext-info) para ver los detalles sobre cmo resolver este problema. o
1 <?xml v e r s i o n= 1 . 0 e n c o d i n g= u t f 8 ?>

Y con esto ya conoces suciente XML como para ser peligroso!

12.3.

La estructura de una fuente de informacin o Atom

Piensa en un blog o en cualquier sitio web que tenga contenido frecuentemente actualizado como CNN.com. El propio sitio dispone de un t tulo (CNN.com), un subt tulo (Breaking News, U.S., World, Weather, Entertaintment y Video News), una fecha de ultima actualizacin (actualizado a 12:43 p.m. EDT, Sat May 16, o 2009) y una lista de art culos publicados en diferente momentos. Cada art culo, a su vez, tiene t tulo, una fecha de primera publicacin (y posiblemente una fecha de o ultima actualizacin, si se public una correccin) y una URL unica. o o o El formato de sindicacin de contenidos Atom est diseado para capturar toda o a n esta informacin en un formato estndar. Mi blog y CNN.com son muy diferentes en o a diseo, ambito y audiencia; pero ambos tienen la misma estructura bsica. CNN.com n a tiene un t tulo, mi blog tiene un t tulo. CNN.com publica art culos, yo publico art culos. En el nivel ms alto existe el elemento raz, que toda fuente Atom comparte: a el elemento feed del espacio de nombres http://www.w3.org/2005/Atom.
1 <f e e d xmlns= h t t p : / /www. w3 . o r g /2005/Atom 2 xml : l a n g= en >

12.3. LA ESTRUCTURA DE UNA FUENTE DE INFORMACION ATOM

261

1. Lnea 1: El espacio de nombres de Atom es http://www.w3.org/2005/Atom. 2. Lnea 2: Cualquier elemento puede contener un atributo xml:lang que sirve para declarar el idioma del elemento y de sus hijos. En este caso, el atributo xml:lang se declara una unica vez en el elemento ra lo que signica que toda z, la fuente se encuentra en ingls. e Una fuente Atom contiene diversas partes de informacin sobre la propia fueno te. Se declaran como hijas del elemento ra feed. z
1 <f e e d xmlns= h t t p : / /www. w3 . o r g /2005/Atom xml : l a n g= en > 2 < t i t l e >d i v e i n t o mark</ t i t l e > 3 <s u b t i t l e >c u r r e n t l y between a d d i c t i o n s </ s u b t i t l e > 4 <id>t a g : d i v e i n t o m a r k . org ,2001 07 29:/ </ id> 5 <updated >2009 03 27T21 : 5 6 : 0 7 Z</updated> 6 <l i n k r e l= a l t e r n a t e type= t e x t / html h r e f= h t t p : / / d i v e i n t o m a r k . o r g / />

1. Lnea 2: El t tulo de esta fuente es dive into mark. 2. Lnea 3: El subt tulo es currently between addictions. 3. Lnea 4: Toda fuente necesita un identicador unico global. Hay que mirar la 1 RFC 4151 para ver cmo crear uno. o 4. Lnea 5: Esta fuente fue actualizada por ultima vez el 27 de marzo de 2009 a las 21:56 GMT. Normalmente es equivalente a la fecha de ultima modicacin o del art culo ms reciente. a 5. Lnea 6: Ahora las cosas comienzan a ponerse interesantes. Esteelemento link no tiene contenido de texto, pero tiene tres atributos: rel, type y href. El valor de rel indica la clase de enlace que es; rel=alternate signica que es un enlace a una representacin alternativa de esta fuente. El atributo type=text/html o signica que es un enlace a una pgina HTML. Por ultimo, el destino del enlace a se indica en el atributo href. Ahora ya conocemos que esta fuente lo es de un sitio denominado dive into mark que est disponible en http://diveintomark.org y que fue actualizada por a ultima vez el 27 de marzo de 2009. Aunque el orden de los elementos puede ser relevante en algunos documentos XML, no es relevante en una fuente Atom.
1

http://www.ietf.org/rfc/rfc4151.txt

262

CAP ITULO 12. XML

Despus de los metadatos de la fuente se encuentra una lista con los art e culos ms recientes. Un art a culo se representa as :
1 <entry > 2 <author> 3 <name>Mark</name> 4 <u r i >h t t p : / / d i v e i n t o m a r k . o r g /</ u r i > 5 </author> 6 < t i t l e >Dive i n t o h i s t o r y , 2009 e d i t i o n </ t i t l e > 7 <l i n k r e l= a l t e r n a t e type= t e x t / html 8 h r e f= h t t p : / / d i v e i n t o m a r k . o r g / a r c h i v e s /2009/03/27/ 9 dive i n t o h i s t o r y 2009 e d i t i o n /> 10 <id>t a g : d i v e i n t o m a r k . org ,2009 03 27:/ a r c h i v e s /20090327172042 </ id> 11 <updated >2009 03 27T21 : 5 6 : 0 7 Z</updated> 12 <p u b l i s h e d >2009 03 27T17 : 2 0 : 4 2 Z</p u b l i s h e d > 13 <c a t e g o r y scheme= h t t p : / / d i v e i n t o m a r k . o r g term= d i v e i n t o p y t h o n /> 14 <c a t e g o r y scheme= h t t p : / / d i v e i n t o m a r k . o r g term= docbook /> 15 <c a t e g o r y scheme= h t t p : / / d i v e i n t o m a r k . o r g term= html /> 16 <summary type= html >P u t t i n g an e n t i r e c h a p t e r on one page sounds 17 b l o a t e d , but c o n s i d e r t h i s &amp ; mdash ; my l o n g e s t c h a p t e r s o f a r 18 would be 75 p r i n t e d pages , and i t l o a d s in under 5 s e c o n d s&amp ; h e l l i p ; 19 On d i a l u p .</summary> 20 </entry >

1. Lnea 2: El elemento author indica quin escribi este art e o culo: un individuo llamado Mark, a quin puedes encontrar en http://diveintomark.org/ (Es el e mismo sitio que el enlace alternativo para la fuente, pero no tiene porqu serlo. e Muchos blogs tienen varios autores, cada uno con su propio sitio web personal). 2. Lnea 6: El elemento title indica el t tulo del art culo. Dive into history, 2009 edition. 3. Lnea 7: Como con el enlace alternativo en el nivel de la fuente, este elemento link indica la direccin de la versin HTML de este art o o culo. 4. Lnea 10: Las entradas, como la fuente, necesitan un identicador unico. 5. Lnea 11: Las entradas tienen dos fechas: la fecha de primera publicacin o (published) y la fecha de ultima modicacin (updated). o 6. Lnea 13: Las entradas pueden tener un nmero arbitrario de categor Este u as. art culo est archivado bajo las categor diveintopython, docbook y html. a as 7. Lnea 16: El elemento summary ofrece un breve resumen del art culo (Existe tambin un elemento content, que no se muestra aqu por si quieres incluir e , el texto completo del art culo en tu fuente). Este resumen tiene el atributo

12.4. ANALISIS DE XML

263

espec co de Atom type=html que indica que este resumen est escrito en a HTML, no es texto plano. Esto es importante puesto que existen entidades espec cas de HTML e el texto (&mdash; y &hellip;) que se deben mostrar como y ... en lugar de que se muestre el texto directamente. 8. Lnea 20: Por ultimo, la etiqueta de cierre del elemento entry, que seala el n nal de los metadatos de este art culo.

12.4.

Anlisis de XML a

Python puede analizar documentos XML de diversas formas. Dispone de analizadores DOM y SAX como otros lenguajes, pero me centrar en una librer diferente e a denominada ElementTree.
1 2 3 4 5 >>> import xml . e t r e e . ElementTree a s e t r e e >>> t r e e = e t r e e . p a r s e ( examples / f e e d . xml ) >>> r o o t = t r e e . g e t r o o t ( ) >>> r o o t <Element { h t t p : / /www. w3 . o r g /2005/Atom} f e e d a t cd1eb0>

1. Lnea 1: La librer ElementTree forma parte de la librer estndar de Python, a a a se encuentra en xml.etree.ElementTree. 2. Lnea 2: El punto de entrada primario de la librer es la funcin parse() que a o puede tomar como parmetro el nombre de un chero o un objeto de ujo. a Esta funcin analiza el documento entero de una vez. Si la memoria es escasa, o existen formas para analiar un documento XML de forma incremental2 . 3. Lnea 3: La funcin parse() devuelve un objeto que representa al documento o completo. No es el elemento ra Para obtener una referencia al elemento ra z. z, debes llamar al mtodo getroot(). e 4. Lnea 4: Como cabr esperar, el elemento ra es el elemento feed del espacio a z de nombres http://www.w3.org/2005/Atom. La representacin en cadena de o texto de este elemento incide en un punto importante: un elemento XML es una combinacin de su espacio de nombres y la etiqueta de su nombre (tambin o e de nominado el nombre local ). todo elemento de este documento se encuentra en el espacio de nombres Atom, por lo que el elemento ra se representa como z {http://www.w3.org/2005/Atom}feed.
2

http://ebot.org/zone/element-iterparse.htm

264

CAP ITULO 12. XML ElementTree represena a los elementos XML como {espacio de nombres}nombre local. Vers y utilizars este formato en muchos lugares de la API de Elementa a Tree.

12.4.1.

Los elementos son listas

En la API de ElementTree los eleemntos se comportan como listas. Los elementos de la lista son los hijos del elemento.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # sigue d e l ejemplo a nt er io r >>> r o o t . t a g { h t t p : / /www. w3 . o r g /2005/Atom} f e e d >>> l e n ( r o o t ) 8 >>> f o r c h i l d in r o o t : ... print ( c h i l d ) ... <Element { h t t p : / /www. w3 . o r g /2005/Atom} t i t l e a t e2b5d0> <Element { h t t p : / /www. w3 . o r g /2005/Atom} s u b t i t l e a t e2b4e0> <Element { h t t p : / /www. w3 . o r g /2005/Atom} i d a t e2b6c0> <Element { h t t p : / /www. w3 . o r g /2005/Atom} updated a t e 2b6 f0 > <Element { h t t p : / /www. w3 . o r g /2005/Atom} l i n k a t e2b4b0> <Element { h t t p : / /www. w3 . o r g /2005/Atom} e n t r y a t e2b720> <Element { h t t p : / /www. w3 . o r g /2005/Atom} e n t r y a t e2b510> <Element { h t t p : / /www. w3 . o r g /2005/Atom} e n t r y a t e2b750>

1. Lnea 2: Continuando con el ejemplo anterior, el elemento ra es {http://www.w3.org/2005/Atom z 2. Lnea 4: La longitud del elemento ra es el nmero de elementos hijo. z u 3. Lnea 6: Puedes utilizar el elemento como iterador para recorrer todos los elementos hijo. 4. Lnea 7: Como ves por la salida, existen ocho elementos hijos: todos los me tadatos de la fuente (title. subtitle, id, updated y link) seguidos por los tres elementos entry. Puede que ya te hayas dado cuenta, pero quiero dejarlo expl cito: la lista de los elementos hijo, unicamente incluye los hijos directos. cada uno de los elementos entry tiene sus propios hijos, pero no se muestran en esta lista. Estarn incluidos en a la lista de hijos del elemento entry, pero no se encuentran en la lista de feed. Existen formas de encontrar elementos independientemente de los profundamente anidados que se encuentren; lo veremos ms adelante en este mismo cap a tulo.

12.5. BUSQUEDA DE NODOS EN UN DOCUMENTO XML

265

12.4.2.

Los atributos son diccionarios

XML no solamente es una coleccin de elementos; cada elemento puede tener o tambin su propio conjunto de atributos. Una vez tienes la referencia a un elemento e espec co puedes recuperar fcilmente sus atributos utilizando un diccionario de a Python.
1 2 3 4 5 6 7 8 9 10 11 12 # sigue d e l ejemplo a nt er io r >>> r o o t . a t t r i b { { h t t p : / /www. w3 . o r g /XML/1998/ namespace } l a n g : en } >>> r o o t [ 4 ] <Element { h t t p : / /www. w3 . o r g /2005/Atom} l i n k a t e181b0> >>> r o o t [ 4 ] . a t t r i b { h r e f : http :// diveintomark . org / , type : t e x t / html , rel : alternate } >>> r o o t [ 3 ] <Element { h t t p : / /www. w3 . o r g /2005/Atom} updated a t e2b4e0> >>> r o o t [ 3 ] . a t t r i b

1. Lnea 2: La propiedad attrib es un diccionario que contiene los atributos del ele mento. El texto XML original era feed xmlns=http://www.w3.org/2005/Atom xml:lang=en. El prejo xml: se reere a un espacio de nombres interno que todo documento XML puede utilizar sin necesidad de declararlo. 2. Lnea 4: El quinto hijo [4] en una lista cuyo primer elemento se cuenta como cero es el elemento link. 3. Lnea 6: El elemento link tiene tres atributos: href, type y rel. 4. Lnea 10: El cuarto hijo [3] es elemento updated. 5. Lnea 12: El elemento updated no tiene atributos por lo que .attrib es un diccionario vac o.

12.5.

B squeda de nodos en un documento XML u

Hasta ahora hemos trabajado con este documento XML de arriba hacia abajo, comenzando por el elemento ra recuperando sus hijos y luego los nietos, etc. z, Pero muchas aplicaciones de XML necesitan encontrar elementos espec cos. ElementTree puede hacer esto tambin. e

266
1 2 3 4 5 6 7 8 9 10 11 12 13

CAP ITULO 12. XML

>>> import xml . e t r e e . ElementTree a s e t r e e >>> t r e e = e t r e e . p a r s e ( examples / f e e d . xml ) >>> r o o t = t r e e . g e t r o o t ( ) >>> r o o t . f i n d a l l ( { h t t p : / /www. w3 . o r g /2005/Atom} e n t r y ) [< Element { h t t p : / /www. w3 . o r g /2005/Atom} e n t r y a t e2b4e0 >, <Element { h t t p : / /www. w3 . o r g /2005/Atom} e n t r y a t e2b510 >, <Element { h t t p : / /www. w3 . o r g /2005/Atom} e n t r y a t e2b540 >] >>> r o o t . t a g { h t t p : / /www. w3 . o r g /2005/Atom} f e e d >>> r o o t . f i n d a l l ( { h t t p : / /www. w3 . o r g /2005/Atom} f e e d ) [] >>> r o o t . f i n d a l l ( { h t t p : / /www. w3 . o r g /2005/Atom} a u t h o r ) []

1. Lnea 4: El mtodo ndall() encuentra todos los elementos hijo que coinciden e con una consulta espec ca (En breve veremos los formatos posibles de la consulta). 2. Lnea 10: Cada elemento incluido el elemento ra pero tambin sus hijos z, e tiene un mtodo ndall(). Permite encontrar todos los elementos que coinciden e entre sus hijos. Pero porqu no devuelve esta consulta ningn resultado? e u Aunque no sea obvio, esta consulta particular unicamente busca entre los hijos del elemento. Puesto que el elemento ra feed no tiene ningn hijo denominado z u feed, esta consulta devuelve una lista vac a. 3. Lnea 12: Tambin te puede sorprender este resultado. Existe un elemento e author en este documento; de hecho hay tres (uno en cada entry). Pero estos elementos author no son hijos directos el elemento ra son nietos (literalz; mente, un elemento hijo de otro elemento hijo). Si quieres buscar elementos author en cualquier nivel de profundidad puedes hacerlo, pero el formato de la consulta es algo distinto.
1 >>> t r e e . f i n d a l l ( { h t t p : / /www. w3 . o r g /2005/Atom} e n t r y ) 2 [< Element { h t t p : / /www. w3 . o r g /2005/Atom} e n t r y a t e2b4e0 >, 3 <Element { h t t p : / /www. w3 . o r g /2005/Atom} e n t r y a t e2b510 >, 4 <Element { h t t p : / /www. w3 . o r g /2005/Atom} e n t r y a t e2b540 >] 5 >>> t r e e . f i n d a l l ( { h t t p : / /www. w3 . o r g /2005/Atom} a u t h o r ) 6 []

1. Lnea 1: Por comodidad, el objeto tree (devuelto por la funcin etree.parse() tie o ne varios mtodos que replican aquellos disponibles en el elemento ra Los ree z. sultados son idnticos a los que se obtienen si se llamase a tree.getroot().ndall(). e

12.5. BUSQUEDA DE NODOS EN UN DOCUMENTO XML

267

2. Linea 5: Tal vez te pueda sorprender, pero esta consulta no encuentra a los elementos author del documento. Porqu no? porque es simplemente una forma e de llamar a tree.getroot().ndall({http://www.w3.org/2005/Atom}author), lo que signica encuentra todos los elementos author que sean hijos directos del elemento ra Los elementos author no son hijos del elemento ra son hijos z. z; de los elementos entry. Por eso la consulta no retorna ninguna coincidencia. Tambin hay un mtodo nd() que retorna el primer elemento que coincide. e e Es util para aquellas situaciones en las que unicamente esperas una coincidencia, o cuando haya varias pero solamente te importa la primera de ellas.
1 2 3 4 5 6 7 8 9 10 >>> e n t r i e s = t r e e . f i n d a l l ( { h t t p : / /www. w3 . o r g /2005/Atom} e n t r y ) >>> l e n ( e n t r i e s ) 3 >>> t i t l e e l e m e n t = e n t r i e s [ 0 ] . f i n d ( { h t t p : / /www. w3 . o r g /2005/Atom} t i t l e ) >>> t i t l e e l e m e n t . t e x t Dive i n t o h i s t o r y , 2009 e d i t i o n >>> f o o e l e m e n t = e n t r i e s [ 0 ] . f i n d ( { h t t p : / /www. w3 . o r g /2005/Atom} f o o ) >>> f o o e l e m e n t >>> type ( f o o e l e m e n t ) <c l a s s NoneType >

1. Lnea 1: Viste esto en el ejemplo anterior. Encuentra todos los elementos atom:entry. 2. Lnea 4: El mtodo nd() toma una consulta y retorna el primer elemento que e coincide. 3. Lnea 7: No existen elementos denominados foo por lo que retorna None. Hay una complicacin en el mtodo nd() que te pasar en algn moo e a u mento. En un contexto booleano los objetos elemento de ElementTree se evalan a False si no tienen hijos (si len(element) es cero). Esto sigu nica que if element.nd(...) no est comprobando si el mtodo nd() a e encontr un elemento coincidente; est comprobando si el elemento coino a cidente tiene algn elemento hijo! Para comprobar si el mtodo nd() u e retorn algn elemento debes utilizar if element.nd(...) is not None. o u Existe una forma de buscar entre los elementos descendientes: hijos, nietos y niveles ms profundos de anidamiento. a

268
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

CAP ITULO 12. XML

>>> a l l l i n k s = t r e e . f i n d a l l ( //{ h t t p : / /www. w3 . o r g /2005/Atom} l i n k ) >>> a l l l i n k s [< Element { h t t p : / /www. w3 . o r g /2005/Atom} l i n k a t e181b0 >, <Element { h t t p : / /www. w3 . o r g /2005/Atom} l i n k a t e2b570 >, <Element { h t t p : / /www. w3 . o r g /2005/Atom} l i n k a t e2b480 >, <Element { h t t p : / /www. w3 . o r g /2005/Atom} l i n k a t e2b5a0 >] >>> a l l l i n k s [ 0 ] . a t t r i b { h r e f : http :// diveintomark . org / , type : t e x t / html , rel : alternate } >>> a l l l i n k s [ 1 ] . a t t r i b { h r e f : h t t p : / / d i v e i n t o m a r k . o r g / a r c h i v e s /2009/03/27/ dive i n t o h i s t o r y 2009 e d i t i o n , type : t e x t / html , rel : alternate } >>> a l l l i n k s [ 2 ] . a t t r i b { h r e f : h t t p : / / d i v e i n t o m a r k . o r g / a r c h i v e s /2009/03/21/ a c c e s s i b i l i t y i s aharsh m i s t r e s s , type : t e x t / html , rel : alternate } >>> a l l l i n k s [ 3 ] . a t t r i b { h r e f : h t t p : / / d i v e i n t o m a r k . o r g / a r c h i v e s /2008/12/18/ g i v e part 1 c o n t a i n e r f o r m a t s , type : t e x t / html , rel : alternate }

1. Lnea 1: Esta consulta //http://www.w3.org/2005/Atomlink es muy simi lar a las anteriores, excepto por las dos barras inclinadas al comienzo de la consulta. Estas dos barras signican que no se busque unicamente entre los hijos directos; quiero cualquier elemento que coincida independientemente del nivel de anidamiento. Por eso el resultado es una lista de cuatro elementos link, no unicamente uno. 2. Lnea 7: El primer resultado es hijo directo del elemento ra Como puedes z. observar por sus atributos, es el enlace alternativo que apunta a la versin o HTML del sitio web que esta fuente describe. 3. Lnea 11: Los otros tres resultados son cada uno de los enlaces alternativos de cada entrada. cada entry tiene un unico elemento hijo link. Debido a la doble barra inclinada al comienzo de la consulta, se encuentran todos estos enlaces. En general, el mtodo ndall() de ElementTree es una caracter e stica muy potente, pero el lenguaje de consulta puede ser un poco sorprendente. Est descrito a ocialmente en http://ebot.org/zone/element-xpath.htm(Soporte limitado a expresiones XPath). XPath es un estndar del W3C para consultar documentos XML. a

12.6. IR MAS ALLA CON LXML

269

El lenguaje de consulta de ElementTree es sucientemente parecido a XPath para poder hacer bsquedas bsicas, pero tambin sucientemente diferente como para u a e desconcertarte si ya conoces XPath. Ahora vamos a ver una librer de terceros que extiende la API de ElementTree a para proporcionar un soporte completo de XPath.

12.6.

Ir ms all con LXML a a

lxml3 es una librer de terceros de cdigo abierto que se desarrolla sobre el a o 4 popular analizador libxml2 . Proporciona una API que es 100 % compatible con ElementTree, y la extiende con soporte completo a Xpath 1.0 y otras cuantas bondades. Existe un instalador disponible para Windows5 ; los usuarios de Linux siempre deber intentar usar las herramientas espec an cas de la distribucin como yum o o apt-get para instalar los binarios precompilados desde sus repositorios. En otro caso, necesitars instalar los binarios manualmente6 . a
1 2 3 4 5 6 7 >>> from lxml import e t r e e >>> t r e e = e t r e e . p a r s e ( examples / f e e d . xml ) >>> r o o t = t r e e . g e t r o o t ( ) >>> r o o t . f i n d a l l ( { h t t p : / /www. w3 . o r g /2005/Atom} e n t r y ) [< Element { h t t p : / /www. w3 . o r g /2005/Atom} e n t r y a t e2b4e0 >, <Element { h t t p : / /www. w3 . o r g /2005/Atom} e n t r y a t e2b510 >, <Element { h t t p : / /www. w3 . o r g /2005/Atom} e n t r y a t e2b540 >]

1. Lnea 1: Una vez importado, lxml proporciona la misma API que la librer a estndar ElementTree. a 2. Lnea 2: La funcin parse(), igual que en ElementTree. o 3. Lnea 3: El mtodo getroot(), tambin igual. e e 4. Lnea 4: El mtodo ndall(), exactamente igual. e Para documentos XML grandes, lxml es signicativamente ms rpido que la a a librer ElementTree. Si solamente ests utilizando la API ElementTree y quieres usar a a la implementacin ms rpida existente, puedes intentar importar lxml y de no estar o a a disponible, usar como segunda opcin ElementTree. o
3 4

http://codespeak.net/lxml/ http://www.xmlsoft.org/ 5 http://pypi.python.org/pypi/lxml/ 6 http://codespeak.net/lxml/installation.html

270
1 2 3 4 try : from lxml import e t r e e except I m p o r t E r r o r : import xml . e t r e e . ElementTree a s e t r e e

CAP ITULO 12. XML

Pero lxml proporciona algo ms que el ser ms rpido que ElementTree. Su a a a mtodo ndall() incluye el soporte de expresiones ms complicadas. e a
1 2 3 4 5 6 7 8 9 10 11 12 13 14 >>> import lxml . e t r e e >>> t r e e = lxml . e t r e e . p a r s e ( examples / f e e d . xml ) >>> t r e e . f i n d a l l ( //{ h t t p : / /www. w3 . o r g /2005/Atom } [ @href ] ) [< Element { h t t p : / /www. w3 . o r g /2005/Atom} l i n k a t eeb8a0 >, <Element { h t t p : / /www. w3 . o r g /2005/Atom} l i n k a t eeb990 >, <Element { h t t p : / /www. w3 . o r g /2005/Atom} l i n k a t eeb960 >, <Element { h t t p : / /www. w3 . o r g /2005/Atom} l i n k a t eeb9c0 >] >>> t r e e . f i n d a l l ( //{ h t t p : / /www. w3 . o r g /2005/Atom} \ [ @href = h t t p : / / d i v e i n t o m a r k . o r g / ] ) [< Element { h t t p : / /www. w3 . o r g /2005/Atom} l i n k a t eeb930 >] >>> NS = { h t t p : / /www. w3 . o r g /2005/Atom} >>> t r e e . f i n d a l l ( //{NS} a u t h o r [ { NS} u r i ] . format (NS=NS ) ) [< Element { h t t p : / /www. w3 . o r g /2005/Atom} a u t h o r a t eeba80 >, <Element { h t t p : / /www. w3 . o r g /2005/Atom} a u t h o r a t eebba0 >]

1. Lnea 1: En este ejemplo voy a importar lxml.tree en lugar de utilizar from lxml import etree, para destacar que estas caracter sticas son espec cas de lxml. 2. Lnea 3: Esta consulta encuentra todos los elementos del espacio de nombres Atom, en cualquier sitio del documento, que contengan el atributo href. Las // al comienzo de la consulta signica elementos en cualquier parte del documento (no unicamente los hijos del elemento ra z). {http://www.w3.org/2005/Atom} signica nicamente los elementos en el espacio de nombres de Atom. * signiu ca elementos con cualquier nombre local y @href signica tiene un atributo href. 3. Lnea 8: La consulta encuentra todos los elementos Atom con el atributo href cuyo valor sea http://diveintomark.org/. 4. Lnea 11: Despus de hacer un rpido formateo de las cadenas de texto (porque e a de otro modo estas consultas compuestas se vuelven rid culamente largas), esta consulta busca los elementos author que tienen un elemento uri como hijo. Solamente retorna dos elementos author, los de la primera y segunda entry. El author del ultimo entry contiene unicamente el elemento name, no uri. No es suciente para t lxml tampoco integra soporte de expresiones XPath ? 1.0. No voy a entrar en profundidad en la sintaxis de XPath; se podr escribir un a libro entero sobre ello. Pero te mostrar cmo se integra en lxml. e o

12.7. GENERACION DE XML


1 2 3 4 5 6 7 8 9 10

271

>>> import lxml . e t r e e >>> t r e e = lxml . e t r e e . p a r s e ( examples / f e e d . xml ) >>> NSMAP = { atom : h t t p : / /www. w3 . o r g /2005/Atom } >>> e n t r i e s = t r e e . xpath ( // atom : c a t e g o r y [ @term= a c c e s s i b i l i t y ] / . . , ... namespaces=NSMAP) >>> e n t r i e s [< Element { h t t p : / /www. w3 . o r g /2005/Atom} e n t r y a t e2b630 >] >>> e n t r y = e n t r i e s [ 0 ] >>> e n t r y . xpath ( . / atom : t i t l e / t e x t ( ) , namespaces=NSMAP) [ A c c e s s i b i l i t y i s a harsh m i s t r e s s ]

1. Lnea 3: Para realizar consultas XPath en elementos de un espacio de nombres, necesitas denir dichos espacios de nombre con el mapeo a sus alias. Esto se realiza con un diccionario de Python. 2. Lnea 4: Esto es una consulta XPath. La expresin XPath busca elementos o category (en el espacio de nombres de Atom) que contengan el atributo term con el valor accesibility. Pero se no es el resultado real de la consulta. Observa el e nal de la cadena de texto de la consulta; observaste el trozo /..? Signica que devuelve el elemento padre del elemento category que se acaba de encontrar. As esta consulta XPath encontrar todas las entradas que tengan un hijo a category term=accessibility. 3. Lnea 6: La funcin xpath() devuelve una lista de objetos ElementTree. En este o documento, unicamente hay una entrada con un elemento category cuyo term sea accesibility. 4. Lnea 9: Las expresiones XPath no siempre devuelven una lista de elementos. Tcnicamente, el modelo DOM de un documento XML no contiene elementos, e contiene nodos. Dependiendo de su tipo, los nodos pueden ser elementos, atributos o incluso contenido de texto. El resultado de una consulta XPath es una lista de nodos. Esta consulta retorna una lista de nodos de texto: el contenido de texto (text()) del elemento title (atom:title) que sea hijo del elemento actual (./).

12.7.

Generacin de XML o

El soporte a XML de Python no est limitado al anlisis de documentos exisa a tentes. Puedes crear tambin documentos XML desde cero. e

272
1 2 3 4 5

CAP ITULO 12. XML

>>> import xml . e t r e e . ElementTree a s e t r e e >>> n e w f e e d = e t r e e . Element ( { h t t p : / /www. w3 . o r g /2005/Atom} f e e d , ... a t t r i b ={ { h t t p : / /www. w3 . o r g /XML/1998/ namespace } l a n g : en } ) >>> print ( e t r e e . t o s t r i n g ( n e w f e e d ) ) <ns0 : f e e d xmlns : ns0= h t t p : / /www. w3 . o r g /2005/Atom xml : l a n g= en />

1. Lnea 2: Para crear un elemento nuevo, se debe instanciar un objeto de la cla se Element. Se le pasa el nombre del elemento (espacio de nombres + nombre local) como primer parmetro. Esta sentencia crear un elemento feed en el esa pacio de nombres Atom. Esta ser nuestro elemento ra del nuevo documento. a z

2. Lnea 3: Para aadir atributos al elemento, se puede pasar un diccionario de n nombres y valores de atributos en el parmetro attrib. Observa que el nombre a del atributo debe estar en el formato estndar de ElementTree, {espacio de nombres}nombre loca a 3. Lnea 4: En cualquier momento puedes serializar cualquier elemento (y sus hijos) con la funcin tostring() de ElementTree. o Te ha sorprendido el resultado de la serializacin? La forma en la que Elemento Tree serializa los elementos con espacios de nombre XML es tcnicamente precisa pero e no optima. El documento XML de ejemplo al comienzo del cap tulo deni un espao cio de nombres por defecto (xmlns=http://www.w3.org/2005/Atom). La denicin o de un espacio de nombres por defecto es util para documentos como las fuentes Atom en los que todos, o la mayor de, los elementos pertenecen al mismo esa pacio de nombres, porque puedes declarar el espacio de nombres una unica vez y declarar cada elemento unicamente con su nombre local (feed, link, entry). No hay necesidad de utilizar prejos a menos que quieras declarar elementos de otro espacio de nombres. Un analizador XML no ver ninguna diferencia entre un documento XML con a un espacio de nombres por defecto y un documento XML con un espacio de nombres con prejo. El DOM resultante de esta serializacin: o
1 <ns0 : f e e d xmlns : ns0= h t t p : / /www. w3 . o r g /2005/Atom xml : l a n g= en />

es idntico al DOM de esta otra: e


1 <f e e d xmlns= h t t p : / /www. w3 . o r g /2005/Atom xml : l a n g= en />

La unica diferencia prctica es que la segunda serializacin es varios caracteres a o ms corta. Si tuvieramos que modicar nuestro ejemplo para aadirle el prejo ns0: a n en cada etiqueta de inicio y n, ser 4 caracteres por cada etiqueta de inicio x 79 an etiquetas + 4 caracteres por la propia declaracin del espacio de nombres, en total o son 320 caracteres ms. En el caso de que asumamos una codicacin de caracteres a o

12.7. GENERACION DE XML

273

UTF8 se trata de 320 bytes extras (despus de comprimir la diferencia se reduce a e 21 bytes). Puede que no te importe mucho, pero para una fuente Atom, que puede descargarse miles de veces cada vez que cambia, una diferencia de unos cuantos bytes por peticin puede suponer una cierta diferencia. o La librer ElementTree no ofrece un control no sobre la serializacin de los a o elementos con espacios de nombres, pero lxml s :
1 2 3 4 5 6 7 8 >>> import lxml . e t r e e >>> NSMAP = {None : h t t p : / /www. w3 . o r g /2005/Atom } >>> n e w f e e d = lxml . e t r e e . Element ( f e e d , nsmap=NSMAP) >>> print ( lxml . e t r e e . t o u n i c o d e ( n e w f e e d ) ) <f e e d xmlns= h t t p : / /www. w3 . o r g /2005/Atom /> >>> n e w f e e d . s e t ( { h t t p : / /www. w3 . o r g /XML/1998/ namespace } l a n g , en ) >>> print ( lxml . e t r e e . t o u n i c o d e ( n e w f e e d ) ) <f e e d xmlns= h t t p : / /www. w3 . o r g /2005/Atom xml : l a n g= en />

1. Lnea 2: Para comenzar, se dene el mapeo de los espacios de nombre como un diccionario. Los valores del diccionario son espacios de nombres; las claves son el prejo deseao. Utilizar None como prejo, sirve para declarar el espacio de nombres por defecto. 2. Lnea 3: Ahora puedes pasar el parmetro nsmap, que es espec a co de lxml, cuando vayas a crear un elemento, y lxml respectar los prejos que hayas a denido. 3. Lnea 4: Como se esperaba, esta serializacin dene el espacio de nombres o Atom como el espacio de nombres por defecto y declara el elemento feed sin prejo. 4. Lnea 6: Ups! Olvidamos aadir el atributo xml:lang. Siempre puedes aadir n n atributos a cualquier elemento con el mtodo set(). Toma dos parmetros, e a el nombre del atributo en formato estndar de ElementTree y el valor del a atributo. Este mtodo no es espec e co de lxml, lo unico espec co de lxml en este ejemplo es la parte del parmetro nsmap para controlar los prejos de la a salida serializada. Estn los documentos XML limitados a un elemento por documento? Por a supuesto que no. Puedes crear hijos de forma fcil. a

274
1 2 3 4 5 6 7 8 9 10 11 12 13

CAP ITULO 12. XML

>>> t i t l e = lxml . e t r e e . SubElement ( new feed , t i t l e , ... a t t r i b ={ type : html } ) >>> print ( lxml . e t r e e . t o u n i c o d e ( n e w f e e d ) ) <f e e d xmlns= h t t p : / /www. w3 . o r g /2005/Atom xml : l a n g= en > < t i t l e type= html /></f e e d > >>> t i t l e . t e x t = d i v e i n t o &h e l l i p ; >>> print ( lxml . e t r e e . t o u n i c o d e ( n e w f e e d ) ) <f e e d xmlns= h t t p : / /www. w3 . o r g /2005/Atom xml : l a n g= en > < t i t l e type= html >d i v e i n t o &amp ; h e l l i p ;</ t i t l e ></f e e d > >>> print ( lxml . e t r e e . t o u n i c o d e ( new feed , p r e t t y p r i n t=True ) ) <f e e d xmlns= h t t p : / /www. w3 . o r g /2005/Atom xml : l a n g= en > < t i t l e type= html >d i v e i n t o&amp ; h e l l i p ;</ t i t l e > </f e e d >

1. Lnea 1: Para crear elementos hijo de un elemento existente, instancia obje tos de la clase SubElement. Los parmetros necesarios son el elemento padre a (new feed en este caso) y el nombre del nuevo elemento. Puesto que los elementos hijo heredan el espacio de nombres de sus padres, no hay necesidad de redeclarar el espacio de nombres o sus prejos. 2. Lnea 2: Puedes pasarle un diccionario de atributos. Las claves son los nombres de los atributos y los valores son los valores de los atributos. 3. Lnea 3: Como esperabas, el nuevo elemento title se ha creado en el espacio de nombres Atom y fue insertado como hijo del elemento feed. Puesto que el elemento title no tiene contenido de texto y no tiene hijos por s mismo, lxml lo serializa como un elemento vac (con /). o 4. Lnea 6: Para establecer el contenido de texto de un elemento basta con asig narle valor a la propiedad .text. 5. Lnea 7: Ahora el elemento title se serializa con su contenido de texto. Cual quier contenido de texto que contenga s mbolos menor que o ampersands necesitan escaparse al serializarse. lxml hace estas conversiones de forma automtica. a 6. Lnea 10: Puedes aplicar una impresin formateada a la serializacin, lo que o o inserta los saltos de l nea correspondientes al cambiar las etiquetas. En trmie nos tcnicos, lxml aade espacios en blanco no signicativos para hacer ms e n a legible la salida resultante. Podr querer echarle un vistazo a xmlwitch7 , otra librer de terceros as a para generar XML. Hace uso extensivo de la sentencia with para hacer la generacin de cdigo XML ms legible. o o a
7

http://github.com/galvez/xmlwitch/tree/master

12.8. ANALISIS DE XML ESTROPEADO

275

12.8.

Anlisis de XML estropeado a

La especicacin XML obliga a que todos los analizadores XML empleen un o manejo de errores draconiano. Esto es, deben parar tan pronto como detecten cualquier clase de error de malformado del documento. Errores de mala formacin del documento son: que las etiquetas de inicio y n no se encuentren bien o balanceadas, entidades sin denir, caracteres unicode ilegales y otro nmero de reu glas esotricas. Esto es un contraste importante con otros formatos habituales como e HTML tu navegador no para de mostrar una pgina web si se te olvida cerrar a una etiqueta HTML o aparece un escape o ampersand en el valor de un atributo (Es un concepto errneo bastante extendido que HTML no tiene denida una forma de o hacer manejo de errores. S que est bien denido, pero es signicativamente ms a a complejo que prate ante el primer error que encuentres. a Algunas personas (yo mismo incluido) creen que fue un error para los inventores del XML obligar a este manejo de errores draconianos. No me malinterpretes; puedo comprender el encanto de la simplicacin de las reglas de manejo de errores. o Pero en la prctica, el concepto de bien formado es ms complejo de lo que suena, a a especialmente para aquellos documentos XML (como los documentos Atom) se publican en la web mediante un servidor HTTP. A pesar de la madurez de XML, cuyo manejo estandarizado de errores es de 1997, las encuestas muestran continuamente que una signicativa fraccin de fuentes Atom de la web estn plagadas con errores o a de buena formacin. o Por eso, tengo razones tericas y prcticas para analizar documentos XML a o a cualquier precio, esto es, para no parar ante el primer error de formacin. Si te o encuentras t mismo en esta situacin, lxml puede ayudar. u o Aqu hay un fragmento de un documento XML mal formado. El ampersand deber estar escapado. a
1 <?xml v e r s i o n= 1 . 0 e n c o d i n g= u t f 8 ?> 2 <f e e d xmlns= h t t p : / /www. w3 . o r g /2005/Atom xml : l a n g= en > 3 < t i t l e >d i v e i n t o &h e l l i p ;</ t i t l e > 4 ... 5 </f e e d >

Eso es un error, porque la entidad &hellip; no est denida en XML (est denia a da en HTML). Si intentas analizar este documento XML con los valores por defecto, lxml parar en la entidad sin denir. a

276

CAP ITULO 12. XML

1 >>> import lxml . e t r e e 2 >>> t r e e = lxml . e t r e e . p a r s e ( examples / f e e d broken . xml ) 3 Traceback ( most r e c e n t c a l l l a s t ) : 4 F i l e <s t d i n > , l i n e 1 , in <module> 5 F i l e lxml . e t r e e . pyx , l i n e 2 6 9 3 , 6 in lxml . e t r e e . p a r s e ( s r c / lxml / lxml . e t r e e . c : 5 2 5 9 1 ) 7 F i l e parser . pxi , l i n e 1478 , 8 in lxml . e t r e e . parseDocument ( s r c / lxml / lxml . e t r e e . c : 7 5 6 6 5 ) 9 F i l e parser . pxi , l i n e 1507 , 10 in lxml . e t r e e . parseDocumentFromURL ( s r c / lxml / lxml . e t r e e . c : 7 5 9 9 3 ) 11 F i l e parser . pxi , l i n e 1407 , 12 in lxml . e t r e e . p a r s e D o c F r o m F i l e ( s r c / lxml / lxml . e t r e e . c : 7 5 0 0 2 ) 13 F i l e parser . pxi , l i n e 965 , 14 in lxml . e t r e e . B a s e P a r s e r . p a r s e D o c F r o m F i l e 15 ( s r c / lxml / lxml . e t r e e . c : 7 2 0 2 3 ) 16 F i l e parser . pxi , l i n e 539 , 17 in lxml . e t r e e . P a r s e r C o n t e x t . h a n d l e P a r s e R e s u l t D o c 18 ( s r c / lxml / lxml . e t r e e . c : 6 7 8 3 0 ) 19 F i l e parser . pxi , l i n e 625 , 20 in lxml . e t r e e . h a n d l e P a r s e R e s u l t ( s r c / lxml / lxml . e t r e e . c : 6 8 8 7 7 ) 21 F i l e parser . pxi , l i n e 565 , 22 in lxml . e t r e e . r a i s e P a r s e E r r o r ( s r c / lxml / lxml . e t r e e . c : 6 8 1 2 5 ) 23 lxml . e t r e e . XMLSyntaxError : 24 E n t i t y h e l l i p not d e f i n e d , l i n e 3 , column 28

Para analizar este documento, a pesar de su error de buena formacin, necesitas o crear un analizador XML espec co.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 >>> p a r s e r = lxml . e t r e e . XMLParser ( r e c o v e r=True ) >>> t r e e = lxml . e t r e e . p a r s e ( examples / f e e d broken . xml , p a r s e r ) >>> p a r s e r . e r r o r l o g examples / f e e d broken . xml : 3 : 2 8 :FATAL:PARSER:ERR UNDECLARED ENTITY: E n t i t y h e l l i p not d e f i n e d >>> t r e e . f i n d a l l ( { h t t p : / /www. w3 . o r g /2005/Atom} t i t l e ) [< Element { h t t p : / /www. w3 . o r g /2005/Atom} t i t l e a t ead510 >] >>> t i t l e = t r e e . f i n d a l l ( { h t t p : / /www. w3 . o r g /2005/Atom} t i t l e ) [ 0 ] >>> t i t l e . t e x t dive into >>> print ( lxml . e t r e e . t o u n i c o d e ( t r e e . g e t r o o t ( ) ) ) <f e e d xmlns= h t t p : / /www. w3 . o r g /2005/Atom xml : l a n g= en > < t i t l e >d i v e i n t o </ t i t l e > . . [ r e s t o de l a s e r i a l i z a c i n s u p r i m i d o por brevedad ] o .

1. Lnea 1: Para crear un analizador espec co, se debe instanciar la clase lxml.etree.XMLParser. Puede recibir un nmero diferente de parmetros. Nos interesa ahora el parmeu a a

12.9. LECTURAS RECOMENDADAS

277

tro recover. Cuando se establece a True, el analizador XML intentar recupea rarse de este tipo de errores. 2. Lnea 2: Para analizar un documento XML con este analizador, basta con pasar este objeto parser como segundo parmetro de la funcin parse(). Observa que a o lxml no eleva ninguna excepcin sobre la entidad no denida &hellip;. o 3. Lnea 3: An as el analizador mantiene un registro de los errores de forma u , cin que ha encontrado (Esto siempre es cierto independientemente de que o est activado para recuperarse de esos errores o no). e 4. Lnea 9: Puesto que no supo que hacer con la entidad sin denir &hellip;, el analizador simplemente la descarta silenciosamente. El contenido de texto del elemento title se convierte en dive into . 5. Lnea 11: Como puedes ver en la serializacin, la entidad &hellip; ha sido o suprimida. Es importante reiterar que no existe garant de interoperabilidad entre a analizadores XML que se recuperan de los errores. Una analizador diferente podr a decidir que reconoce la entidad &hellip; de HTML y reemplazarla por &amp;hellip;? Es esto mejor? Puede ser. Es ms correcto? No, ambas soluciones son igualmente a errneas. El comportamiento correcto (de acuerdo a la especicacin XML) es pararse o o y elevar el error. Si has decidido que no es lo que quieres hacer, lo haces bajo tu propia responsabilidad.

12.9.

Lecturas recomendadas

XML en la wikipedia.org: http://en.wikipedia.org/wiki/XML La API de ElementTree: Elementos y rboles de elementos a http://ebot.org/zone/element.htm Soporte de XPath en ElementTree http://ebot.org/zone/element-xpath.htm La funcin iterparse de ElementTree o http://ebot.org/zone/element-iterparse.htm

278 lxml http://codespeak.net/lxml/ Anlisis de XML y HTML con lxml a http://codespeak.net/lxml/1.3/parsing.html XPath y XSLT con lxml http://codespeak.net/lxml/1.3/xpathxslt.html xmlwitch http://github.com/galvez/xmlwitch/tree/master

CAP ITULO 12. XML

Cap tulo 13 Serializacin de Objetos en o Python


Nivel de dicultad:  Desde que vivimos en este apartamento, cada sbado me he levantado a las 6:15, a me he preparado un tazn de cereales con leche, o me he sentado en este lado de este sof, he puesto la BBC America, y he visto a Doctor Who. Sheldon, La teor del Big Bang.1 a

13.1.

Inmersin o

El concepto de la serializacin es simple. Tienes una estructura de datos en o memoria que quieres grabar, reutilizar o enviar a alguien. Cmo lo haces? Bueno, o eso depende de lo que quieras grabar, de cmo lo quieras reutilizar y a quin se lo o e env Muchos juegos te permiten grabar el avance cuando sales de ellos, y continuar as. en donde lo dejaste cuando los vuelves a cargar (En realidad, esto tambin lo hacen e las aplicaciones que no son de juegos). En estos casos, se necesita almacenar en disco una estructura de datos que almacena tu grado de avance hasta el momento, cuando los juegos se reinician, es necesario volver a cargar estas estructuras de datos. Los datos, en estos casos, slo se utilizan por el mismo programa que los cre, no o o se env por la red ni se leen por nadie ms que por el programa que los cre. an a o Por ello, los posibles problemas de interoperabilidad quedan reducidos a asegurar
1

http://en.wikiquote.org/wiki/The Big Bang Theory#The Dumpling Paradox .5B1.07.5D

279

280

CAP ITULO 13. SERIALIZACION DE OBJETOS EN PYTHON

que versiones posteriores del mismo programa pueden leer los datos escritos por versiones previas. Para casos como estos, el mdulo pickle es ideal. Forma parte de la librer o a estndar de Python, por lo que siempre est disponible. Es rpido, la mayor parte a a a est escrito en C, como el propio intrprete de Python. Puede almacenar estructuras a e de datos de Python todo lo complejas que se necesite. Qu puede almacenar el mdulo pickle? e o Todos los tipos de datos nativos que Python soporta: booleanos, enteros, nmeu ros de coma otante, nmeros complejos, cadenas, objetos bytes, arrays de byte u y None. Listas, tuplas, diccionarios y conjuntos que contengan cualquier combinacin o de tipos de dato nativos. Listas, tuplas, diccionarios y conjuntos de datos que contengan cualquier combinacin de listas, tiplas, diccionarios y conjuntos conteniendo cualquier como binacin de tipos de datos nativos (y as sucesivamente, hasta alcanzar un o mximo nivel de anidamiento2 ). a Funciones, clases e instancias de clases (con ciertas limitaciones). Si no es suciente para ti, el mdulo pickle se puede extender. Si ests interesado o a en la extensibilidad, revisa los enlaces de la seccin de Lecturas recomendadas al nal o de este cap tulo.

13.1.1.

Una nota breve sobre los ejemplos de este cap tulo

Este cap tulo cuenta una historia con dos consolas de Python. Todos los ejemplos de este cap tulo son parte de una unica historia. Se te pedir que vayas pasando a de una consola a otra de Python para demostrar el funcionamiento de los mdulos o pickle y json. Para ayudarte a mantener las cosas claras, abre la consola de Python y dene la siguiente variable:
1 >>> s h e l l = 1

Mantn la ventana abierta. Ahora abre otra consola de Python y dene la e siguiente variable:
2

http://docs.python.org/3.1/library/sys.html#sys.getrecursionlimit

13.2. ALMACENAMIENTO DE DATOS A UN FICHERO PICKLE


1 >>> s h e l l = 2

281

A lo largo de este cap tulo, utilizar la variable shell para inidcar en qu consola e e de Python se ejecuta cada ejemplo.

13.2.

Almacenamiento de datos a un chero pickle

El mdulo pickle funciona con estructuras de datos. Vamos a construir una. o


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 >>> 1 >>> >>> >>> shell

e n t r y = {} e n t r y [ t i t l e ] = Dive i n t o h i s t o r y , 2009 e d i t i o n entry [ a r t i c l e l i n k ] = http :// diveintomark . org / + \ a r c h i v e s /2009/03/27/ d ive i n t o h i s t o r y 2009 e d i t i o n >>> e n t r y [ c o m m e n t s l i n k ] = None >>> e n t r y [ i n t e r n a l i d ] = b \xDE\xD5\xB4\xF8 >>> e n t r y [ t a g s ] = ( d i v e i n t o p y t h o n , docbook , html ) >>> e n t r y [ p u b l i s h e d ] = True >>> import time >>> e n t r y [ p u b l i s h e d d a t e ] = \ time . s t r p t i m e ( F r i Mar 27 2 2 : 2 0 : 4 2 2009 ) >>> e n t r y [ p u b l i s h e d d a t e ] time . s t r u c t t i m e ( tm year =2009 , tm mon=3, tm mday=27 , tm hour =22 , tm min =20 , t m s e c =42 , tm wday=4, tm yday =86 , t m i s d s t =1)

1. Lnea 1: Teclalo en la consolo #1. e 2. Lnea 3: La idea aqu es construir un diccionario de Python que pueda repre sentar algo que sea util, como una entrada de una fuente Atom. Pero tambin e quiero asegurarme de que contiene diferentes tipos de datos para mostrar el funcionamiento del mdulo pickle. No entres demasiado en los valores concreo tos. 3. Lnea 13: El mdulo time contiene una estructura de datos (time struct) que o representa un punto en el tiempo (con una precisin de milisegundo) y funcioo nees que sirven para manipular estructuras de este tipo. La funcin strptime() o recibe una cadena formateada y la convierte en una estructura time struct. Esta cadena se encuentra en el formato por defecto, pero puedes controlarlo con los cdigos de formato. Para ms detalles consulta el mdulo time3 . o a o
3

http://docs.python.org/3.1/library/time.html

282

CAP ITULO 13. SERIALIZACION DE OBJETOS EN PYTHON

Bueno, ya tenemos un estupendo diccionario de Python. Vamos a salvarlo en un chero.


1 >>> s h e l l 2 1 3 >>> import p i c k l e 4 >>> with open ( e n t r y . p i c k l e , wb ) a s f : 5 ... p i c k l e . dump( entry , f ) 6 ...

1. Lnea 1: Seguimos en la consola #1. 2. Lnea 4: Utiliza la funcin open() para abrir un chero. El modo de apertura o es wb, de escritura y en binario. Lo envolvemos en una sentencia with para asegurar que el chero se cierra automticamente al nalizar su uso. a 3. Lnea 5: La funcin dump() del mdulo pickle toma una estructura de datos o o serializable de Python y la serializa a un formato binario, espec co de Python, y almacena el resultado en el chero abierto. Esa ultima sentencia era muy importante. El mdulo pickle toma una estructura de datos Python y la salva a un chero. o Para hacer esto, serializa la estructura de datos utilizando un formato de datos denominado el protocolo pickle. Este protocolo es espec co de Python; no existe ninguna garant de compaa tibilidad entre lenguajes de programacin. Probablemente no puedas abrir el o chero entry.pickle con Perl, PHP, Java u otro lenguaje. No todas las estructuras de datos de Python se pueden serializar con el mdulo o pickle. El protocolo pickle ha cambiado varias veces para acomodar nuevos tipos de datos que se han ido aadiendo a Python, pero an tiene limitaciones. n u Como resultado de estos cambios, no existe garant de compatibilidad entre a diferentes versiones de Python. Las versiones nuevas de Python soportan los formatos antiguos de serializacin, pero las versiones viejas de Python no soo portan los formatos nuevos (puesto que no soportan los tipos de datos nuevos). A menos que especiques otra cosa, las funciones del mdulo pickle utilizarn o a la ultima versin del protocolo pickle. Esto asegura que dispones de la mxima o a exibilidad en los tipos de datos que puedes serializar, pero tambin signica e que el chero resultante no podr leerse en versiones de Python ms antiguas a a que no soporten la ultima versin del protocolo. o

13.3. CARGA DE DATOS DE UN FICHERO PICKLE

283

La ultima versin del protocolo pickle es un formato binario. Asegrate de abrir o u el chero en modo binario o los datos se corrompern durante la escritura. a

13.3.

Carga de datos de un chero pickle

Ahora cambia a la segunda consola de Python la otra, la que no utilizaste para crear el diccionario entry.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 >>> s h e l l 2 >>> e n t r y Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> NameError : name e n t r y i s not d e f i n e d >>> import p i c k l e >>> with open ( e n t r y . p i c k l e , rb ) a s f : ... entry = p i c k l e . load ( f ) ... >>> e n t r y { c o m m e n t s l i n k : None , i n t e r n a l i d : b \xDE\xD5\xB4\xF8 , t i t l e : Dive i n t o h i s t o r y , 2009 e d i t i o n , t a g s : ( d i v e i n t o p y t h o n , docbook , html ) , article link : h t t p : / / d i v e i n t o m a r k . o r g / a r c h i v e s /2009/03/27/ dive i n t o h i s t o r y 2009 e d i t i o n , p u b l i s h e d d a t e : time . s t r u c t t i m e ( tm year =2009 , tm mon=3, tm mday=27 , tm hour =22 , tm min =20 , t m s e c =42 , tm wday=4, tm yday =86 , t m i s d s t = 1), p u b l i s h e d : True }

1. Lnea 1: Consola #2. 2. Lnea 3: La variable entry no est denida en esta consola. La denimos en la a consola #1, que se trata de un entorno totalmente separado de ste y tiene su e propio estado. 3. Lnea 8: Se abre el chero entry.pickle que creamos con la consola #1. El mdulo pickle utiliza un formato binario, por lo que siempre hay que abrir los o cheros de este tipo en modo binario. 4. Lnea 9: La funcin pickle.load() toma un objeto stream, lee los datos seriali o zados del stream, crea un nuevo objeto Python, recrea los datos serializados en el nuevo objeto Python y devuelve el objeto.

284

CAP ITULO 13. SERIALIZACION DE OBJETOS EN PYTHON

5. Lnea 11: Ahora la variable entry contiene un diccionario con las claves y valores que nos son familiares de la otra consola. El ciclo pickle.dump() / pickle.load() da como resultado una estructura de datos nueva que es igual a la original.
1 2 3 4 5 6 7 8 9 10 11 12 13 >>> s h e l l 1 >>> with open ( e n t r y . p i c k l e , rb ) a s f : ... entry2 = p i c k l e . load ( f ) ... >>> e n t r y 2 == e n t r y True >>> e n t r y 2 i s e n t r y False >>> e n t r y 2 [ t a g s ] ( d i v e i n t o p y t h o n , docbook , html ) >>> e n t r y 2 [ i n t e r n a l i d ] b \xDE\xD5\xB4\xF8

1. Lnea 1: Volvemos a la consola #1. 2. Lnea 3: Abrimos el chero entry.pickle. 3. Lnea 4: Cargamos los datos serializados en la nueva variable entry2. 4. Lnea 6: Python conrma que los dos diccionarios, entry y entry2, son iguales. En esta consola, construimos el diccionario almacenado en entry desde cero, creando un diccionario vac y aadindole valores poco a poco. Serializamos el o n e diccionario y lo almacenamos en el chero entry.pickle. Ahora hemos recuperado los datos serializados desde el chero y hemos creado una rplica perfecta de e la estructura de datos original. 5. Lnea 8: Igualdad no es lo mismo que identidad. Como he dicho, hemos creado una rplica perfecta de los datos originales. Pero son una copia. e 6. L nea 10: Por razones que aclarar ms tarde en el cap e a tulo, he querido mostrar que el valor de la clave tags es una tupla, y el valor de la clave internal id es un objeto bytes.

13.4. SERIALIZACION CON PICKLE SIN PASAR POR UN FICHERO

285

13.4.

Serializacin con pickle sin pasar por un o chero

Los ejemplos de la seccin anterior te mostraron cmo serializar un objeto o o Python directamente a un chero en disco. Pero qu sucede si no necesitas un e chero? Puedes serializar a un objeto bytes en memoria.
1 2 3 4 5 6 7 8 >>> s h e l l 1 >>> b = p i c k l e . dumps ( e n t r y ) >>> type ( b ) <c l a s s b y t e s > >>> e n t r y 3 = p i c k l e . l o a d s ( b ) >>> e n t r y 3 == e n t r y True

1. Lnea 3: La funcin pickle.dumps() (observa la s al nal del nombre de la fun o cin) realiza la misma serializacin que la funcin pickle.dump(). Pero en lugar o o o de tomar como parmetro un objeto stream y serializar sobre l, simplemente a e retorna los datos serializados. 2. Lnea 4: Puesto que el protocolo pickle utiliza un formato de datos binario, la funcin pickle.dumps() retorna un objeto bytes. o 3. Lnea 6: La funcin pickle.loads() (de nuevo, observa la s al nal del nombre o de la funcin) realiza la misma operacin que la funcin pickle.load(). Pero en o o o lugar de tomar como parmetro un objeto stream y leer de l los datos, toma a e un objeto bytes que contenga datos serializados. 4. Lnea 7: El resultado nal es el mismo: una rplica perfecta del diccionario e original.

13.5.

Los bytes y las cadenas de nuevo vuelven sus feas cabezas

El protocolo pickle existe desde hace muchos aos, y ha madurado a la par n que lo ha hecho Python. Por ello, actualmente existen cuatro versiones diferentes del protocolo. Python 1.x ten dos protocolos, un formato basado en texto (versin 0) y a o un formato binario (versin 1). o

286

CAP ITULO 13. SERIALIZACION DE OBJETOS EN PYTHON Python 2.3 introdujo un protocolo nuevo (versin 2) para tener en cuenta o la nueva funcionalidad de clases y objetos en Python. Es un formato binario. Python 3.0 introdujo otro protocolo (versin 3) que soporta expl o citamente los objetos bytes y arrays de byte. Es un formato binario.

Como puedes observar, la diferencia existente entre cadenas de texto y bytes vuelve a aparecer (si te sorprende es que no has estado poniendo atencin). En la o prctica, signica que mientras que Python 3 puede leer datos almacenados con el a protocolo versin 2, Python 2 no puede leer datos almacenados con el protocolo o versin 3. o

13.6.

Depuracin de cheros pickle o

A qu se parece el protocolo pickle? Vamos a salir un momento de la consola e de Python y echarle un vistazo al chero entry.pickle que hemos creado.
1 2 3 4 5 6 7 8 9 10

rwr r

y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ l s l e n t r y . p i c k l e 1 you you 358 Aug 3 1 3 : 3 4 e n t r y . p i c k l e y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ c a t e n t r y . p i c k l e comments linkqNXtagsqXdiveintopythonqXdocbookqXhtmlq ?qX p u b l i s h e d q ? XlinkXJhttp : / / d i v e i n t o m a r k . o r g / a r c h i v e s /2009/03/27/ dive i n t o h i s t o r y 2009 e d i t i o n q Xpublished dateq ctime struct time ? qRqXtitleqXDive i n t o h i s t o r y , 2009 e d i t i o n q u .

No ha sido muy util. Puedes ver las cadenas de texto, pero los otros tipos de da to salen como caracteres ilegibles. Los campos no estn delimitados por tabuladores a ni espacios. No se trata de un formato que quieras depurar por ti mismo.

13.6. DEPURACION DE FICHEROS PICKLE


1 >>> s h e l l 2 1 3 >>> import p i c k l e t o o l s 4 >>> with open ( e n t r y . p i c k l e , rb ) a s f : 5 ... pickletools . dis ( f ) 6 0 : \ x80 PROTO 3 7 2: } EMPTY DICT 8 3: q BINPUT 0 9 5: ( MARK 10 6: X BINUNICODE p u b l i s h e d d a t e 11 25: q BINPUT 1 12 27: c GLOBAL time s t r u c t t i m e 13 45: q BINPUT 2 14 47: ( MARK 15 48: M BININT2 2009 16 51: K BININT1 3 17 53: K BININT1 27 18 55: K BININT1 22 19 57: K BININT1 20 20 59: K BININT1 42 21 61: K BININT1 4 22 63: K BININT1 86 23 65: J BININT 1 24 70: t TUPLE (MARK a t 4 7 ) 25 71: q BINPUT 3 26 73: } EMPTY DICT 27 74: q BINPUT 4 28 7 6 : \ x86 TUPLE2 29 77: q BINPUT 5 30 79: R REDUCE 31 80: q BINPUT 6 32 82: X BINUNICODE c o m m e n t s l i n k 33 100: q BINPUT 7 34 102: N NONE 35 103: X BINUNICODE i n t e r n a l i d 36 119: q BINPUT 8 37 121: C SHORT BINBYTES xxxx 38 127: q BINPUT 9 39 129: X BINUNICODE t a g s 40 138: q BINPUT 10 41 140: X BINUNICODE d i v e i n t o p y t h o n 42 159: q BINPUT 11 43 161: X BINUNICODE docbook 44 173: q BINPUT 12 45 175: X BINUNICODE html 46 184: q BINPUT 13 47 1 8 6 : \ x87 TUPLE3 48 187: q BINPUT 14 49 189: X BINUNICODE t i t l e 50 199: q BINPUT 15 51 201: X BINUNICODE Dive i n t o h i s t o r y , 2009 e d i t i o n 52 237: q BINPUT 16 53 239: X BINUNICODE a r t i c l e l i n k

287

288
1 2 3 4 5 6 7 8 9 10 256: q 258: X

CAP ITULO 13. SERIALIZACION DE OBJETOS EN PYTHON

BINPUT 17 BINUNICODE h t t p : / / d i v e i n t o m a r k . o r g / a r c h i v e s /2009/ 03/27/ div e i n t o h i s t o r y 2009 e d i t i o n 337: q BINPUT 18 339: X BINUNICODE p u b l i s h e d 353: q BINPUT 19 3 5 5 : \ x88 NEWTRUE 356: u SETITEMS (MARK a t 5 ) 357: . STOP h i g h e s t p r o t o c o l among o p c o d e s = 3

La informacin ms interesante de este volcado es la que aparece en la ultima o a l nea, ya que muestra la versin del protocolo de pickle con la que el chero se o grab. No existe un marcador de versin expl en el protocolo de pickle. Para o o cio determinar la versin del protocolo, se observan los marcadores (cdigos de operacin o o o - opcodes) existentes en los datos almacenados y se utiliza el conocimiento expreso de qu cdigos fueron introducidos en cada versin del protocolo pickle. La funcin e o o o pickle.dis() hace exactamente eso e imprime el resultado en la ultima l nea del volcado de salida. La siguiente es una funcin que simplemente devuelve el nmero de versin o u o sin imprimir nada:
1 2 3 4 5 6 7 import p i c k l e t o o l s def p r o t o c o l v e r s i o n ( f i l e o b j e c t ) : maxproto = 1 f o r opcode , arg , pos in p i c k l e t o o l s . genops ( f i l e o b j e c t ) : maxproto = max( maxproto , opcode . p r o t o ) return maxproto

Y aqu la vemos en accin: o


1 >>> import p i c k l e v e r s i o n 2 >>> with open ( e n t r y . p i c k l e , rb ) a s f : 3 ... v = pickleversion . protocol version ( f ) 4 >>> v 5 3

13.7.

Serializacin de objetos Python para caro garlos en otros lenguajes

El formato utilizado por el mdulo pickle es espec o co de Python. No intenta ser compatible con otros lenguajes de programacin. Si la compatibilidad entre o

13.8. ALMACENAMIENTO DE DATOS EN UN FICHERO JSON

289

lenguajes es un requisito, necesitas utilizar otros formatos de serializacin. Uno de o ellos es JSON4 . JSON signica JavaScript Object Notation - Notacin de Obo jetos JavaScript, pero no dejes que el nombre te engae JSON est diseado n a n expl citamente para permitir su uso en diferentes lenguajes de programacin. o Python 3 inclyye un mdulo json en su librer estndar. Como el mdulo pickle, o a a o el mdulo json dispone de funciones para la serializacin de estructuras de datos, o o almacenamiento de los datos serializados en disco, carga de los mismos y deserializacin en un nuevo objeto Python. Pero tambin tiene importantes diferencias. La o e primera es que JSON es un formato de datos textual, no binario. La especicacin o 5 RFC 4627 dene el formato y cmo se codican los diferentes tipos de datos de o forma textual. Por ejemplo, un valor booleano se almacena como la cadena texto de cinco caracteres false o como la cadena de texto de cuatro caracters true. Todos los valores de JSON tienen en cuenta las maysculas y minsculas. u u Segundo, como cualquier formato basado en texto, existe el problema de los espacios en blanco. JSON permite el uso de un nmero arbitrario de espacios en u blanco (espacios, tabuladores, retornos de carro y saltos de l nea) entre los valores. Estos espacios en blanco son no signicativos, lo que signica que los codicadores de JSON pueden aadir tantos como deseen, y los decodicadores de JSON estn n a obligados a ignorarlos siempre que se encuentren entre dos valores. Esto permite que los datos de un chero JSON se puedan imprimir bien formateados, anidando de forma clara los valores que se encuentran dentro de otros para que puedas verlos bien en un editor o visor de texto estndar. El mdulo json de Python dispone de a o opciones para codicar la salida con formato apropiado para la lectura. Tercero, existe el problema perenne de la codicacin de caracteres. Puesto que o JSON codica los valores como texto plano, pero como ya sabes, no existe tal texto plano. JSON debe almacenarse con caracteres Unicode (UTF-32, UTF-16 o, por defecto, UTF-8), y la seccin 3 de la RFC-46276 , dene cmo indicar qu codicacin o o e o se est utilizando. a

13.8.

Almacenamiento de datos en un chero JSON

JSON se parece mucho a una estructura de datos que pudieras denir en JavaScript. No es casualidad, en realidad, puedes utilizar la funcin eval() de JavaScript o para decodicar los datos serializados en JSON. Lo fundamental es conocer que
4 5

http://json.org/ http://www.ietf.org/rfc/rfc4627.txt 6 http://www.ietf.org/rfc/rfc4627.txt

290

CAP ITULO 13. SERIALIZACION DE OBJETOS EN PYTHON

JSON forma parte del propio lenguaje JavaScript. Como tal, JSON puede que ya te sea familiar.
1 2 3 4 5 6 7 8 9 10 11 >>> 1 >>> >>> >>> >>> >>> >>> >>> >>> ... shell b a s i c e n t r y = {} b a s i c e n t r y [ i d ] = 256 b a s i c e n t r y [ t i t l e ] = Dive i n t o h i s t o r y , 2009 e d i t i o n b a s i c e n t r y [ t a g s ] = ( d i v e i n t o p y t h o n , docbook , html ) b a s i c e n t r y [ p u b l i s h e d ] = True b a s i c e n t r y [ c o m m e n t s l i n k ] = None import j s o n with open ( b a s i c . j s o n , mode= w , e n c o d i n g= u t f 8 ) a s f : j s o n . dump( b a s i c e n t r y , f )

1. Lnea 3: Vamos a crear una nueva estructura de datos, en lugar de reutilizar la estructura de datos entry preexistente. Despus veremos qu sucede cuando e e intentamos codicar en JSON la otra estructura de datos ms compleja. a 2. Lnea 10: JSON est basado en texto, lo que signica que es necesario abrir a el chero en modo texto y especicar una codicacin de caracteres. Nunca te o equivocars utilizando UTF-8. a 3. Lnea 11: Como con el mdulo pickle, el mdulo json dene la funcin dump() o o o que toma una estructura de datos Python y un objeto de ujo (stream) con permisos de escritura. La funcin dump() serializa la estructura de datos de o Python y escribe el resultado en el objeto de ujo. Al hacerlo dentro de una sentencia with nos aseguramos de que el chero quede cerrado correctamente cuando hayamos terminado. Cmo queda el resultado serializado? o
1 2 3 4 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ c a t b a s i c . j s o n { p u b l i s h e d : t r u e , t a g s : [ d i v e i n t o p y t h o n , docbook , html ] , comments link : null , id : 256 , t i t l e : Dive i n t o h i s t o r y , 2009 e d i t i o n }

Es ms legible que el chero en formato de pickle. Pero como JSON puede a contener tantos espacios en blanco como se desee entre diferentes valores, y el mdulo o json proporciona una forma sencilla de utilizar esta capacidad, podemos crear cheros JSON an ms legibles. u a
1 >>> s h e l l 2 1 3 >>> with open ( b a s i c p r e t t y . j s o n , mode= w , e n c o d i n g= u t f 8 ) a s f : 4 ... j s o n . dump( b a s i c e n t r y , f , i n d e n t =2)

13.9. MAPEO DE LOS TIPOS DE DATOS DE PYTHON A JSON

291

Si se pasa el parmetro indent a la funcin json.dump() el chero JSON resula o tante ser ms legible an. A costa de un chero de tamao mayor. El parmetro a a u n a indent es un valor entero en el que 0 signica pon cada valor en su propia l nea y un nmero mayor que cero signica pon cada valor en su propia l u nea, y utiliza este nmero de espacios para indentar las estructuras de datos anidadas. u Por lo que ste es el resultado: e
1 2 3 4 5 6 7 8 9 10 11 12 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ c a t b a s i c p r e t t y . j s o n { published : true , tags : [ diveintopython , docbook , html ], comments link : null , id : 256 , t i t l e : Dive i n t o h i s t o r y , 2009 e d i t i o n }

13.9.

Mapeo de los tipos de datos de Python a JSON

Puesto que JSON no es espec co de Python, existen algunas diferencias en su cobertura de los tipos de dato de Python. Algunas de ellas son simplemente de denominacin, pero existen dos tipos de dato importantes de Python que no existen o en JSON. Observa esta tabla a ver si los echas de menos: Notas JSON Python 3 object dictionary array list string string integer integer real number oat * true True * false False * null None * Las maysculas y minsculas en los valores JSON son signicativas. u u Te has dado cuenta de lo que falta? Tuplas y bytes! JSON tiene un tipo de datos array, al que se mapean las listas de Python, pero no tiene un tipo de datos

292

CAP ITULO 13. SERIALIZACION DE OBJETOS EN PYTHON

separado para los arrays congelados (tuplas). Y aunque JSON soporta cadenas de texto, no tiene soporte para los objetos bytes o arrays de bytes.

13.10.

Serializacin de tipos no soportados en JSON o

Incluso aunque JSON no tiene soporte intr nseco de bytes, es posible serializar objetos bytes. El mdulo json proporciona unos puntos de extensibilidad para o codicar y decodicar tipos de dato desconocidos (Por desconocido se entiende en este contexto a aquellos tipos de datos que no estn denidos en la especicacin a o de JSON). Si quieres codicar bytes u otros tipos de datos que JSON no soporte de forma nativa, necesitas proporcionar codicadores de decodicadores a medida para esos tipos de dato.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 >>> s h e l l 1 >>> e n t r y { c o m m e n t s l i n k : None , i n t e r n a l i d : b \xDE\xD5\xB4\xF8 , t i t l e : Dive i n t o h i s t o r y , 2009 e d i t i o n , t a g s : ( d i v e i n t o p y t h o n , docbook , html ) , a r t i c l e l i n k : h t t p : / / d i v e i n t o m a r k . o r g / a r c h i v e s /2009/03 /27/ dive i n t o h i s t o r y 2009 e d i t i o n , p u b l i s h e d d a t e : time . s t r u c t t i m e ( tm year =2009 , tm mon=3, tm mday=27 , tm hour =22 , tm min =20 , t m s e c =42 , tm wday=4, tm yday =86 , t m i s d s t = 1), p u b l i s h e d : True } >>> import j s o n >>> with open ( e n t r y . j s o n , w , e n c o d i n g= u t f 8 ) a s f : ... j s o n . dump( entry , f ) ... Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 5 , in <module> F i l e C: \ Python31 \ l i b \ j s o n \ i n i t . py , l i n e 1 7 8 , in dump f o r chunk in i t e r a b l e : F i l e C: \ Python31 \ l i b \ j s o n \ e n c o d e r . py , l i n e 4 0 8 , in i t e r e n c o d e f o r chunk in i t e r e n c o d e d i c t ( o , c u r r e n t i n d e n t l e v e l ) : F i l e C: \ Python31 \ l i b \ j s o n \ e n c o d e r . py , l i n e 3 8 2 , in i t e r e n c o d e d i c t f o r chunk in chunks : F i l e C: \ Python31 \ l i b \ j s o n \ e n c o d e r . py , l i n e 4 1 6 , in i t e r e n c o d e o = default (o) F i l e C: \ Python31 \ l i b \ j s o n \ e n c o d e r . py , l i n e 1 7 0 , in d e f a u l t r a i s e TypeError ( r e p r ( o ) + i s not JSON s e r i a l i z a b l e ) TypeError : b \xDE\xD5\xB4\xF8 i s not JSON s e r i a l i z a b l e

1. Lnea 3: Ok, es el momento de volver a la estructura de datos entry. Tiene de

13.10. SERIALIZACION DE TIPOS NO SOPORTADOS EN JSON

293

todo: un valor booleano, un None, una cadena de texto, una tupla de cadenas de texto, un objeto bytes y una estructura time. 2. Lnea 15: S lo que he dicho antes, pero vamos a repetirlo: JSON es un formato e de texto. Siempre se deben abrir los cheros JSON en modo texto con la codicacin de caracteres UTF-8. o 3. Lnea 18: Error! Qu ha pasado? e Lo que ha pasado es que la funcin json.dump() intent serializar el objeto o o bytes pero fall porque JSON no dispone de soporte de objetos bytes. Sin embargo, o si es importante almacenar bytes en este formato, puedes denir tu propio formato de serializacin. o
1 2 3 4 5 def t o j s o n ( p y t h o n o b j e c t ) : i f i s i n s t a n c e ( python object , bytes ) : return { c l a s s : bytes , v a l u e : l i s t ( python object )} r a i s e TypeError ( r e p r ( p y t h o n o b j e c t ) + i s not JSON s e r i a l i z a b l e )

1. Lnea 1: Para denir un formato de serializacin para un tipo de datos que o JSON no soporte de forma nativa, simplemente dene una funcin que tome un o objeto Python como parmetro. Este objeto ser el que la funcin json.dump() a a o sea incapaz de serializar de forma nativa en este caso el objeto bytes. 2. Lnea 2: La funcin debe validar el tipo de datos que recibe. No es estricta o mente necesario pero as queda totalmente claro que casos cubre esta funcin, o y hace ms sencillo ampliarla ms tarde. a a 3. Lnea 4: En este caso, he elegido convertir el objeto bytes en un diccionario. La a clave class guardar el tipo de datos original (como una cadena, bytes), a y la clave value guardar el valor real. Como hay que convertirlo a algo que pueda serializarse en JSON, no se puede guardar directamente el objeto bytes. Como un objeto bytes es una secuencia de nmeros entereos; con cada entero u entre el 0 y el 255, podemos utilizar la funcin list() para convertir el objeto o bytes en una lista de enteros. De forma que el objeto bzxDEzxD5zx84zxF8 se convierte en [222, 213, 180, 248]. Por ejemplo, el byte zxDE en hexadecimal, se convierte en 222 en decimal, zxD5 es 213 y as cada uno de ellos. 4. Lnea 5: Esta l nea es importante. La estructura de datos que ests serializando a puede contener tipos de dato que ni el serializador interno del mdulo de o Python ni el tuyo puedan manejar. En este caso, tu serializador debe elevar una excepcin TypeError para que la funcin json.dump() sepa que tu serializador o o no reconoci el tipo de dato del objeto. o

294

CAP ITULO 13. SERIALIZACION DE OBJETOS EN PYTHON

Y eso es todo, no necesitas hacer nada ms. En particular, esta funcin a a o medida retorna un un diccionario de Python, no una cadena. No ests haciendo la a serializacin a JSON completa por ti mismo; solamente la parte correspondiente a o un tipo de datos que no est soportado de forma nativa. La funcin json.dump() a o har el resto. a
1 >>> s h e l l 2 1 3 >>> import c u s t o m s e r i a l i z e r 4 >>> with open ( e n t r y . j s o n , w , e n c o d i n g= u t f 8 ) a s f : 5 ... j s o n . dump( entry , f , d e f a u l t=c u s t o m s e r i a l i z e r . t o j s o n ) 6 ... 7 Traceback ( most r e c e n t c a l l l a s t ) : 8 F i l e <s t d i n > , l i n e 9 , in <module> 9 j s o n . dump( entry , f , d e f a u l t=c u s t o m s e r i a l i z e r . t o j s o n ) 10 F i l e C: \ Python31 \ l i b \ j s o n \ i n i t . py , l i n e 1 7 8 , in dump 11 f o r chunk in i t e r a b l e : 12 F i l e C: \ Python31 \ l i b \ j s o n \ e n c o d e r . py , l i n e 4 0 8 , in i t e r e n c o d e 13 f o r chunk in i t e r e n c o d e d i c t ( o , c u r r e n t i n d e n t l e v e l ) : 14 F i l e C: \ Python31 \ l i b \ j s o n \ e n c o d e r . py , l i n e 3 8 2 , in i t e r e n c o d e d i c t 15 f o r chunk in chunks : 16 F i l e C: \ Python31 \ l i b \ j s o n \ e n c o d e r . py , l i n e 4 1 6 , in i t e r e n c o d e 17 o = default (o) 18 F i l e / U s e r s / p i l g r i m / d i v e i n t o p y t h o n 3 / examples / c u s t o m s e r i a l i z e r . py , 19 l i n e 1 2 , in t o j s o n 20 r a i s e TypeError ( r e p r ( p y t h o n o b j e c t ) + i s not JSON s e r i a l i z a b l e ) 21 TypeError : time . s t r u c t t i m e ( tm year =2009 , tm mon=3, tm mday=27 , tm hour =22 , 22 tm min =20 , t m s e c =42 , tm wday=4, tm yday =86 , t m i s d s t =1) 23 i s not JSON s e r i a l i z a b l e

1. Lnea 3: El mdulo customserializer es el lugar en el que has denido la funcin o o to json() del ejemplo anterior. 2. Lnea 4: El modo texto, la codicacin UTF-8, etc. Lo olvidars! a veces lo o a olvidars! Y todo funcionar hasta el momento en que falle, y cuando falle, lo a a har de forma espectacular. a 3. Lnea 5: Este es el trozo importante: asignar una funcin de conversin ad-hoc o o en la funcin json.dump(), hay que pasar tu funcin a la funcin json.dump() o o o en el parmetro default. a 4. Lnea 20: Ok, realmente no ha funcionado. Pero observa la excepcin. La o funcin json.dump() ya no se queja ms sobre el objeto de tipo bytes. Ahora se o a est quejando sobre un objeto totalmente diferente, el objeto time.struct time. a Aunque obtener una excepcin diferente podr no parecer mucho progreso lo o a es! Haremos una modicacin ms para superar este error: o a

13.10. SERIALIZACION DE TIPOS NO SOPORTADOS EN JSON


1 2 3 4 5 6 7 8 9 10 import time

295

def t o j s o n ( p y t h o n o b j e c t ) : i f i s i n s t a n c e ( p y t h o n o b j e c t , time . s t r u c t t i m e ) : return { c l a s s : time . a s c t i m e , v a l u e : time . a s c t i m e ( p y t h o n o b j e c t ) } i f i s i n s t a n c e ( python object , bytes ) : return { c l a s s : bytes , v a l u e : l i s t ( python object )} r a i s e TypeError ( r e p r ( p y t h o n o b j e c t ) + i s not JSON s e r i a l i z a b l e )

1. Lnea 4: Aadimos cdigo a nuestra funcin customserializer.to json(), necesi n o o tamos validar que el objeto Python sea time.struct time (aqul con el que la e funcin json.dump() est teniendo problemas). o a 2. Lnea 6: Haremos una conversin parecida a la que hicimos con el objeto o bytes: convertir el objeto time.struct time en un diccionario que solamente contenga valores serializables en JSON. En este caso, la forma ms sencilla de a convertir una fecha/hora a JSON es convertirlo en una cadena con la funcin o time.asctime(). La funcin time.asctime() convertir la estructura en la cadena o a Fri Mar 27 22:20:42 2009. Con estas dos conversiones a medida, la estructura completa de datos entry deber serializarse a JSON sin ms problemas. a a
1 >>> s h e l l 2 1 3 >>> with open ( e n t r y . j s o n , w , e n c o d i n g= u t f 8 ) a s f : 4 ... j s o n . dump( entry , f , d e f a u l t=c u s t o m s e r i a l i z e r . t o j s o n ) 5 ... 1 2 3 4 5 6 7 8 9 10 11 12 y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ l s l example . j s o n 1 you you 391 Aug 3 1 3 : 3 4 e n t r y . j s o n y o u @ l o c a l h o s t : / d i v e i n t o p y t h o n 3 / examples$ c a t example . j s o n { p u b l i s h e d d a t e : { c l a s s : time . a s c t i m e , v a l u e : F r i Mar 27 2 2 : 2 0 : 4 2 2009 } , comments link : null , i n t e r n a l i d : { c l a s s : bytes , v a l u e : [ 2 2 2 , 213 , 180 , 248]} , t a g s : [ d i v e i n t o p y t h o n , docbook , html ] , t i t l e : Dive i n t o h i s t o r y , 2009 e d i t i o n , a r t i c l e l i n k : http :// diveintomark . org / a r c h i v e s / 2009/03/27/ dive i n t o h i s t o r y 2009 e d i t i o n , published : true }

rwr r

296

CAP ITULO 13. SERIALIZACION DE OBJETOS EN PYTHON

13.11.

Carga de datos desde un chero JSON

Como el mdulo pickle, el mdulo json tiene una funcin load() que toma un o o o objeto de ujo de datos y lee la informacin formateada en JSON y crea un objeto o Python que es idntico a la estructura de datos JSON. e
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 >>> s h e l l 2 >>> del e n t r y >>> e n t r y Traceback ( most r e c e n t c a l l l a s t ) : F i l e <s t d i n > , l i n e 1 , in <module> NameError : name e n t r y i s not d e f i n e d >>> import j s o n >>> with open ( e n t r y . j s o n , r , e n c o d i n g= u t f 8 ) a s f : ... entry = json . load ( f ) ... >>> e n t r y { c o m m e n t s l i n k : None , internal id : { class : bytes , v a l u e : [ 2 2 2 , 213 , 180 , 248]} , t i t l e : Dive i n t o h i s t o r y , 2009 e d i t i o n , t a g s : [ d i v e i n t o p y t h o n , docbook , html ] , a r t i c l e l i n k : http :// diveintomark . org / a r c h i v e s / 2009/03/27/ dive i n t o h i s t o r y 2009 e d i t i o n , published date : { c l a s s : time . a s c t i m e , v a l u e : F r i Mar 27 2 2 : 2 0 : 4 2 2009 } , p u b l i s h e d : True }

1. Lnea 3: Con nes demostrativos, pasamos a la consola #2 y borramos la estructura de datos entry que hab amos creado antes con el mdulo pickle. o 2. Lnea 10: En el caso ms simple, la funcin json.load() funciona de la misma a o forma que la funcin pickle.load(). Le pasamos un ujo de datos y devuelve un o objeto Python nuevo. 3. Lnea 12: Tengo buenas y malas noticias. Las buenas primero: la funcin o json.load() carga satisfactoriamente el cheroentry.json que has creado en la consola #1 y crea un nuevo objeto Python que contiene la informacin. Ahora o las malas noticias: No recrea la estructura de datos entry original. Los dos valores internal id y published date se han recreado como diccionarios espec camente, los diccionarios con valores compatibles JSON que creamos en la funcin de conversin to json(). o o La funcin json.load() no sabe nada sobre ninguna funcin de conversin que o o o puedas haber pasado a la funcin json.dump(). Lo que se necesita es la funcin o o

13.11. CARGA DE DATOS DESDE UN FICHERO JSON

297

opuesta a to json() una funcin que tomar un objeto JSON convertido a medida o a y convertir de nuevo a Python el tipo de datos original. a
1 # add t h i s t o c u s t o m s e r i a l i z e r . py 2 def f r o m j s o n ( j s o n o b j e c t ) : 3 if class in j s o n o b j e c t : 4 if json object [ class ] == time . a s c t i m e : 5 return time . s t r p t i m e ( j s o n o b j e c t [ v a l u e ] ) 6 if json object [ class ] == b y t e s : 7 return b y t e s ( j s o n o b j e c t [ v a l u e ] ) 8 return j s o n o b j e c t

1. Lnea 2: Esta funcin de conversin tambin toma un parmetro y devuelve o o e a un valor. Pero el parmetro que toma no es una cadena, es un objeto Python a el resultado de deserializar la cadena JSON en un objeto Python. 2. Lnea 3: Lo unico que hay que hacer es validar si el objeto contiene la clave o o , class que cre la funcin to json(). Si es as el valor de la clave class te dir cmo decodicar el valor en su tipo de datos original de Python. a o 3. Lnea 5: Para decodicar la cadena de texto que que devolvi la funcin ti o o me.asctime(), utilizamos la funcin time.strptime(). Esta funcin toma una cao o dena de texto con formato de fecha y hora (en un formato que se puede adaptar, pero que tiene el formato por defecto de la funcin time.asctime()) y devuelve o un objeto time.struct time. 4. Lnea 7: Para convertir de nuevo la lista de enteros a un objeto bytes puedes utilizar la funcin bytes(). o Eso es todo. Solamente se manejaban dos tipos de dato en la funcin to json(), o y ahora son esos dos tipos de dato los que se manejan en la funcin from json(). Este o es el resultado:

298
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

CAP ITULO 13. SERIALIZACION DE OBJETOS EN PYTHON

>>> s h e l l 2 >>> import c u s t o m s e r i a l i z e r >>> with open ( e n t r y . j s o n , r , e n c o d i n g= u t f 8 ) a s f : ... e n t r y = j s o n . l o a d ( f , o b j e c t h o o k=c u s t o m s e r i a l i z e r . f r o m j s o n ) ... >>> e n t r y { c o m m e n t s l i n k : None , i n t e r n a l i d : b \xDE\xD5\xB4\xF8 , t i t l e : Dive i n t o h i s t o r y , 2009 e d i t i o n , t a g s : [ d i v e i n t o p y t h o n , docbook , html ] , a r t i c l e l i n k : http :// diveintomark . org / a r c h i v e s / 2009/03/27/ dive i n t o h i s t o r y 2009 e d i t i o n , p u b l i s h e d d a t e : time . s t r u c t t i m e ( tm year =2009 , tm mon=3, tm mday=27 , tm hour =22 , tm min =20 , t m s e c =42 , tm wday=4, tm yday =86 , t m i s d s t = 1), p u b l i s h e d : True }

1. Lnea 5: Para utilizar la funcin from json() durante el proceso de deserializa o o cin, hay que pasarla en el parmetro object hook a la funcin json.load(). Una o a funcin que toma como parmetro a otra funcin es muy util! o a o 2. Lnea 7: La estructura de datos entry ahora contiene uan clave internal id que tiene como valor a un objeto bytes. Y tambin contiene una clave publise hed date cuyo valor es un objeto time.struct time. Sin embargo, an queda un pequeo tema por tratar. u n
1 2 3 4 5 6 7 8 9 10 11 12 >>> s h e l l 1 >>> import c u s t o m s e r i a l i z e r >>> with open ( e n t r y . j s o n , r , e n c o d i n g= u t f 8 ) a s f : ... e n t r y 2 = j s o n . l o a d ( f , o b j e c t h o o k=c u s t o m s e r i a l i z e r . f r o m j s o n ) ... >>> e n t r y 2 == e n t r y False >>> e n t r y [ t a g s ] ( d i v e i n t o p y t h o n , docbook , html ) >>> e n t r y 2 [ t a g s ] [ d i v e i n t o p y t h o n , docbook , html ]

1. Lnea 7: Incluso despus de utilizar la funcin to json() en la serializacin y e o o la funcin from json() en la deserializacin, an no hemos recreado la rplica o o u e perfecta de la estructura original porqu no? e

13.12. LECTURAS RECOMENDADAS

299

2. Lnea 9: En la estructura de datos original el valor de la clave tags era una tupla de tres cadenas. 3. Lnea 11: Pero en la estructura entry2 el valor de la clave tags es una lista de tres cadenas. JSON no distingue entre tuplas y listas; solamente tiene un tipo de datos parecido a la lista, el array, y el mdulo json de Python cono vierte calladamente ambos tipos, listas y tuplas, en arrays de JSON durante la serializacin. Para la mayor de usos, es posible ignorar esta diferencia, pero o a conviene saberlo cuando se utiliza este mdulo json. o

13.12.

Lecturas recomendadas

Muchos art culos del mdulo pickle hacen referencia a cPickle. En Python o 2 existen dos implementaciones del mdulo pickle, uno escrito en Python o puro y otro escrito en C (pero que se puede llamar desde Python). En Python 3 se han consolidado ambos mdulos, por lo que siempre deber o as utilizar import pickle. Sobre el mdulo pickle: o el mdulo pickle: o http://docs.python.org/3.1/library/pickle.html pickle y cPickle serializacin de objetos en Python: o http://www.doughellmann.com/PyMOTW/pickle/ Utilizacin de pickle: o http://wiki.python.org/moin/UsingPickle Gestin de la persistencia en Python: o http://www.ibm.com/developerworks/library/l-pypers.html Sobre el mdulo json: o json Serializador de la notacin de objetos de JavaScript: o http://www.doughellmann.com/PyMOTW/json/ Codicacin y decodicacin JSON en Python utilizando objetos a medida: o o http://blog.quaternio.net/2009/07/16/json-encoding-and-decoding-with-customobjects-in-python/

300

CAP ITULO 13. SERIALIZACION DE OBJETOS EN PYTHON Sobre la extensibilidad de pickle: Sobre el almacenamiento de instancias de clase: http://docs.python.org/3.1/library/pickle.html#pickling-class-instances Persistencia de objetos externos: http://docs.python.org/3.1/library/pickle.html#persistence-of-external-objects Manejo de objetos con estado: http://docs.python.org/3.1/library/pickle.html#handling-stateful-objects

Cap tulo 14 Servicios Web HTTP


Nivel de dicultad:  Una mente revuelta hace la almohada incmoda. o Charlotte Bront e

14.1.

Inmersin o

Los servicios web HTTP permiten el env y recepcin de informacin desde o o o servidores remotos, sin utilizar nada ms que operaciones HTTP. Si quieres recuperar a informacin desde un servidor, utiliza HTTP GET; si quieres enviar informacin al o o servidor, utiliza HTTP POST. Los servicios web HTTP ms avanzados disponen de a APIs avanzadas que permiten crear, modiar y borrar informacin, utilizando HTTP o PUT y HTTP DELETE. En otras palabras, los verbos construidos en el protocolo HTTP (GET, POST, PUT y DELETE) pueden mapearse directamente en operaciones de nivel de aplicacin para recuperar, crear, modicar y borrar datos. o La principal ventaja de esta aproximacin es la simplicidad, y esta simplio cidad se ha demostrado que es muy popular. La informacin normalmente en o XML o JSON puede estar construida y almacenada estticamente, o generada a dinmicamente por un programa del servidor y todos los lenguajes de programacin a o importantes (incluido Python desde luego!) incluyen una librer HTTP para desa cargarla. La depuracin tambin es ms sencilla, porque cada recurso de un servicio o e a web HTTP dispone de una direccin unica (en forma de URL), puedes cargarla en o tu navegador web e inmediatamente ver la informacin obtenida. o Son ejemplos de servicios web HTTP:

301

302

CAP ITULO 14. SERVICIOS WEB HTTP

1. Las APIs de datos de Google1 te permiten interactuar con una amplia variedad de servicios de Google, incluidos Blogger2 y YouTube3 . 2. Los servicios de Flickr4 te permiten cargar y descargar fotos de Flickr. 3. La API de Twitter5 te permite publicar actualizaciones de estado en Twitter. 4. ...y muchos ms6 . a Python 3 dispone de dos librer diferentes para interactuar con los servicios as web HTTP: 1. http.client7 es una librer de bajo nivel que implementa el protocolo HTTP8 . a 2. urllib.request9 es una capa de abstraccin construida sobre http.client. Proo porciona una API estndar para acceder a servidores HTTP y FTP, sigue a automticamente redirecciones HTTP y maneja algunas formas comunes de a autenticacin HTTP. o Cul deberiamos usar? Ninguna de ellas. En su lugar deber utilizar htta as plib2 , una librer de cdigo abierto de terceros que implementa HTTP de forma a o ms completa que http.client y proporciona una mejor abstraccin que urllib.request. a o
10

Para comprender porqu httplib2 es la eleccin correcta necesitas comprender e o primero HTTP.

14.2.

Caracter sticas de HTTP

Hay cinco caracter sticas importantes que todos los clientes HTTP deber an soportar.
http://code.google.com/apis/gdata/ http://www.blogger.com/ 3 http://www.youtube.com/ 4 http://www.ickr.com/services/api/ 5 http://apiwiki.twitter.com/ 6 http://www.programmableweb.com/apis/directory/1?sort=mashups 7 http://docs.python.org/3.1/library/http.client.html 8 RFC 2616:http://www.w3.org/Protocols/rfc2616/rfc2616.html 9 http://docs.python.org/3.1/library/urllib.request.html 10 http://code.google.com/p/httplib2/
2 1

14.2. CARACTER ISTICAS DE HTTP

303

14.2.1.

Cach e

Lo ms importante que se debe comprender para usar cualquier servicio web a es que el acceso a travs de la web es increiblemente costoso. No quiero decir que e cueste en euros y cntimos (aunque el ancho de banda no sea gratis). Quiero decir e que lleva mucho tiempo abrir una conexin, enviar una peticin y recuperar una o o respuesta de un servidor remoto. Incluso en la conexin ms rpida existente, la o a a latencia (el tiempo que tarda desde el env de una peticin hasta que se inicia la o o recogida de los datos en la respuesta) puede ser mayor de lo que puedas esperar. Un router puede tener un malfuncionamiento, un paquete se pierde, un proxy est bajo a ataque nunca existe un momento de aburrimiento en la red de Internet y no puedes hacer nada contra esto. HTTP est diseado con la posibilidad de a n cacheo en mente. Existe toda una clase de disCache-Control: max-age signipositivos (llamados proxys cach) cuyo unico e ca no me moleste hasta dentrabajo consiste en interponerse entre ti y el resto tro de una semana del mundo para minimizar el acceso a la red. Tu empresa o tu proveedor de servicios de Internet es muy probable que tengan proxys de este tipo, incluso aunque no seas consciente de ello. Funcionan gracias al sistema de cach construido en el protocolo HTTP. e Veamos un ejemplo concreto sobre cmo funciona la cach. Visita diveintoo e mark.org en tu navegador. Esta pgina incluye una imagen de fondo wearehugh.com/m.jpg. a Cuando tu navegador descarga esa imagen, el servidor incluye las siguientes cabeceras HTTP:
1 HTTP/ 1 . 1 200 OK 2 Date : Sun , 31 May 2009 1 7 : 1 4 : 0 4 GMT 3 S e r v e r : Apache 4 Last M o d i f i e d : Fri , 22 Aug 2008 0 4 : 2 8 : 1 6 GMT 5 ETag : 3075 ddc8d800 6 Accept Ranges : b y t e s 7 Content Length : 12405 8 CacheC o n t r o l : maxage =31536000 , p u b l i c 9 E x p i r e s : Mon, 31 May 2010 1 7 : 1 4 : 0 4 GMT 10 Connection : c l o s e 11 Content Type : image / j p e g

Las cabeceras Cache-Control y Expires le dicen a tu navegador (y a cualquier proxy con cach entre t y el servidor) que esta imagen puede cachearse durante e u un ao. un ao! y si, durante el prximo ao, visitas alguna pgina que incluya un n n o n a enlace a esta imagen, tu navegador cargar la imagen desde su cach sin generar a e ninguna actividad en la red.

304

CAP ITULO 14. SERVICIOS WEB HTTP

Pero espera, que es an mejor. Digamos que tu navegador borra la imagen de u su cach local por alguna razn. Puede que se le acabe el espacio que tiene reservado e o en el disco, puede que t borres manualmente la cach. Lo que sea. Pero las cabeceras u e HTTP dicen que esta informacin se puede almacenar en cualquier cach pblica11 . o e u Los proxies con cach estn diseados para disponer de toneladas de espacio de e a n almacenamiento, probablemete mucho ms del que dispone tu navegador. a Si tu compa o proveedor de Interne disponen de un proxy con cach, el proxy na e puede que tenga todav la informacin disponible en la cach. Cuando visites de a o e nuevo diveintomark.org tu navegador buscar la imagen en la cach local, si no la a e encuentra har una peticin a la red para intentar descargarla del servidor remoto. a o Pero si el proxy an dispone de una copia de la imagen, interceptar la peticin y u a o servir la imagen desde la cach. Esto signica que tu peticin nunca alcanzar el a e o a servidor remoto. De hecho, nunca saldr de la red de tu compa Esto hace que a na. la descarga sea ms rpida (menos saltos en la red) y ahorra dinero a tu compa a a na (menos informacin descargndose del mundo exterior). o a El sistema de cach de HTTP unicamente funciona cuando todas las partes hae cen su trabajo. Por una parte, los servidores deben enviar las cabeceras correctas en su respuesta. Por otra parte, los clientes deben comprender y respetar las cabeceras antes de solicitar la misma informacin dos veces. Los proxys que se encuentren en o medio del camino no son la panacea; dependen de los servidores y de los clientes. Las librer de Python de HTTP no soportan la cach, pero la librer httplib2 as e a s .

14.2.2.

Comprobacin de la ultima vez que se modic una o o pgina a

Alguna informacin nunca cambia, mientras que otra cambia constantemente. o Entre ambos extremos existe un amplio campo de datos que podra haber cambiado, pero no lo ha hecho. El ujo de informacin de la CNN se actualiza cada pocos o minutos, pero mi blog puede que no cambie en d o semanas. En el ultimo caso, as no quiero decirle a los clientes que cacheen las pginas durante semanas, porque a cuando realmente pongo una nueva entrada, la gente no la leer hasta pasadas a unas semanas (porque estar respetando mis cabeceras de cach que dir no te an e an preocupes de validar durante semanas). Por otra parte, no quiero que los clientes se estn descargando mi ujo completo una vez cada hora si no ha cambiado. e
Tcnicamente lo importante es que la cabecera Cache-Control no tiene la clave private, por lo e que esta informacin se puede almacenar en una cach por defecto. o e
11

14.2. CARACTER ISTICAS DE HTTP

305

El protocolo HTTP tiene una solucin para o 304: Not Modied signica esto. Cuando solicitas datos por primera vez, el la misma mierda en distinto servidor puede enviar una cabecera denominada d a. Last-Modied. Esta cabecera indica lo que dice: la fecha en la que la informacin fue modicada. o La imagen de fondo de diveintomark.org inclu una cabecera Last-Modied a
1 HTTP/ 1 . 1 200 OK 2 Date : Sun , 31 May 2009 1 7 : 1 4 : 0 4 GMT 3 S e r v e r : Apache 4 Last M o d i f i e d : Fri , 22 Aug 2008 0 4 : 2 8 : 1 6 GMT 5 ETag : 3075 ddc8d800 6 Accept Ranges : b y t e s 7 Content Length : 12405 8 CacheC o n t r o l : maxage =31536000 , p u b l i c 9 E x p i r e s : Mon, 31 May 2010 1 7 : 1 4 : 0 4 GMT 10 Connection : c l o s e 11 Content Type : image / j p e g

Cuando solicitas la misma informacin una segunda vez (o tercera o cuarta), o puedes enviar una cabecera If-Modied-Since con la peticin, con la fecha que recupeo raste desde el servidor la ultima vez. Si la informacin ha cambiado desde entonces, o el servidor ignora la cabecera If-Modied-Since y devuelve la nueva informacin con o un cdigo de estado 200. Pero si los datos no han cambiado desde entonces, el servio dor env un cdigo de estado especial HTTP 304 que signica estos datos no han a o cambiado desde la ultima vez que los pediste. Puedes probar esto en la l nea de comando utilizando la sentencia curl12 :
1 y o u @ l o c a l h o s t : $ c u r l I H I f Modified S i n c e : 2 Fri , 22 Aug 2008 0 4 : 2 8 : 1 6 GMT h t t p : / / wearehugh . com/m. j p g 3 HTTP/ 1 . 1 304 Not M o d i f i e d 4 Date : Sun , 31 May 2009 1 8 : 0 4 : 3 9 GMT 5 S e r v e r : Apache 6 Connection : c l o s e 7 ETag : 3075 ddc8d800 8 E x p i r e s : Mon, 31 May 2010 1 8 : 0 4 : 3 9 GMT 9 CacheC o n t r o l : maxage =31536000 , p u b l i c

Porqu se trata de una mejora? Porque cuando el servidor env un cdigo e a o 304, no reenva la informacin. Lo unico que se obtiene es el cdigo de estado. o o Incluso despus de que tu copia de cach haya expirado, la comprobacin de la e e o ultima fecha de modicacin te asegura que no descargas la misma informacin dos o o veces si no ha cambiado (Como bono extra, esta respuesta 304 tambin incluye las e cabeceras de cach. Los proxys mantendrn una copia de los datos incluso despus e a e
12

http://curl.haxx.se/

306

CAP ITULO 14. SERVICIOS WEB HTTP

de que hayan expirado ocialmente, con la esperanza de que los datos no hayan cambiado realmente y que la siguiente peticin responda con un cdigo de estado o o 304 y la informacin de cach actualizada). o e Las librer HTTP de Python no soportan la comprobacin de la ultima fecha as o de modicacin, pero la librer httplib2 s lo hace. o a

14.2.3.

Cach de ETag e

Las ETags son una forma alternativa de conseguir lo mismo que con la validacin de la ultima fecha de modicacin. En este caso, el servidor env un cdigo hash o o a o en una cabecera ETag junto con los datos que hayas solicitado (La forma exacta por la que el servidor calcula este hash la determina el propio servidor. El unico requi sito es que cambie cuando cambie la informacin). La imagen de fondo referenciada o desde diveintomark.org ten un cdigo ETag. a o
1 HTTP/ 1 . 1 200 OK 2 Date : Sun , 31 May 2009 1 7 : 1 4 : 0 4 GMT 3 S e r v e r : Apache 4 Last M o d i f i e d : Fri , 22 Aug 2008 0 4 : 2 8 : 1 6 GMT 5 ETag : 3075 ddc8d800 6 Accept Ranges : b y t e s 7 Content Length : 12405 8 CacheC o n t r o l : maxage =31536000 , p u b l i c 9 E x p i r e s : Mon, 31 May 2010 1 7 : 1 4 : 0 4 GMT 10 Connection : c l o s e 11 Content Type : image / j p e g

La segunda vez que solicites la misma informacin, incluirs el cdigo ETag en o a o una cabecera If-None-Match. Si la informacin no ha cambiado, el servidor enviar un o a cdigo de estado 304. Como en el caso de la comprobacin de la fecha de ultima o o modicacin, el servidor unicamente env el cdigo de estado 304; no env la misma o a o a informacin una segunda vez. Al incluir un cdigo Etag en tu segunda peticin, o o o le ests diciendo al servidor que no existe necesidad de volver a enviar la misma a informacin si an coincide con este hash, puesto que an tienes la informacin o u u o desde la ultima vez. De nuevo con curl: Etag signica no hay nada nuevo bajo el sol

14.2. CARACTER ISTICAS DE HTTP


1 y o u @ l o c a l h o s t : $ c u r l I H I f NoneMatch : \3075 ddc8d800 \ 2 h t t p : / / wearehugh . com/m. j p g 3 HTTP/ 1 . 1 304 Not M o d i f i e d 4 Date : Sun , 31 May 2009 1 8 : 0 4 : 3 9 GMT 5 S e r v e r : Apache 6 Connection : c l o s e 7 ETag : 3075 ddc8d800 8 E x p i r e s : Mon, 31 May 2010 1 8 : 0 4 : 3 9 GMT 9 CacheC o n t r o l : maxage =31536000 , p u b l i c

307

Las ETag se suelen encerrar entre comillas, pero las comillas forman parte del valor. Esto signica que necesitas enviar al servidor esas comillas en la cabecera If-None-Match. Las librer de Python HTTP no soportan ETags, pero httplib2 s as .

14.2.4.

Compresin o

Cuando hablamos de los servicios web HTTP, siempre se suele hablar de informacin de texto que va y viene a travs de la red. Puede que sea XML, puede que o e sea JSON o unicamente texto plano. Independientemente del formato, el texto se comprime bastante bien. En el ujo de ejemplo del cap tulo sobre XML la longitud del texto sin comprimir es de 3070 bytes, pero ser 941 bytes despus de aplicar an e una compresin gzip. El 30 % del tamao original! o n HTTP soporta varios algoritmos de compresin13 . Los dos ms comunes son o a gzip y deate. Cuando solicitas un recurso sobre HTTP puedes pedirle al servidor que lo env en formato comprimido. Puedes incluir una cabecera Accept-encoding en e tu peticin que liste qu algoritmos de compresin soportas. Si el servidor soporta o e o alguno de los algoritmos te enviar la informacin comprimida (con una cabecea o ra Content-encoding que indica el algoritmo que se ha utilizado). Ya solamente te quedar descomprimir los datos. a Una pista importante para los desarrolladores del lado del servidor: debe asegurarse que la versin comprimida de un recurso tiene diferente ETag o que la versin descomprimida. De otro modo, los proxys de cach se cono e fundirn y pueden servir la versin comprimida a clientes que no pueden a o manejarla. Lee la discusin de un error de Apache (nmero 3972714 ) para o u ms detalles sobre este sutil asunto. a Las librer HTTP de Python no soportan compresin, httplib2 s as o .
13 14

http://www.iana.org/assignments/http-parameters https://issues.apache.org/bugzilla/show bug.cgi?id=39727

308

CAP ITULO 14. SERVICIOS WEB HTTP

14.2.5.

Redireccionamiento

Las URIs buenas no cambian, pero muchas no lo son. Los sitios web se reorganizan, las pginas se mueven a nuevas direcciones, incluso los servicios web se puea den reorganizar. Un ujo de informacin sindicada en http://example.com/index.xml o podr moverse a http://example.com/xml/atom.xml. O el dominio completo podr a a moverse, segn una organizacin pueda expandirse y reorganizarse http://example.com/index.xml u o se podr convertir en http://server-farm-1.example.com/index.xm. a Cada vez que solicitas alguna clase de recurso de un servidor HTTP, el servidor incluye un cdigo de estado en su respuesta. El cdigo de estado 200 signica o o todo es normal, aqu est la pgina solicitada. El cdigo de estado 404 signica a a o pgina no encontrada (probabblemente te ha pasado alguna vez mientras navegaa bas en Internet). Los cdigos de estado de la gama de los 300 indican algn tipo de o u redireccionamiento. HTTP dispone de varias formas de indicar que un recurso se ha movido. Las dos tcnicas e Location signica mira ms habituales son los cdigos de estado 302 y a o aqu . 301. El cdigo de estado 302 es una redireccin o o temporal ; lo que signica uh! se ha movido temporalmente a otra direccin (y luego se indica la direccin temporal en la cabecera o o Location). El cdigo de estado 301 es una redireccin permanente; signica uh! se o o ha movido permanentemente (y luego indica la nueva direccin en una cabecera Loo cation). Si el cdigo de estado es 302 y una nueva direccin, la especicacin HTTP o o o indica que deber utilizar la nueva direccin para obtener lo que has solicitdo, as o pero la siguiente vez que quieras acceder al mismo recurso, deber reintentarlo con as la vieja direccin. Pero si lo que obtienes es un codigo de estado 301 y una nueva o direccin, se supone que debes usar la nueva direccin a partir de ese momento. o o El mdulo urllib.request sigue automticamente los redireccionamientos cuano a do recibe los cdigos de estado indicados desde el servidor HTTP, pero no te indica o que lo haya hecho. Obtendrs los datos que solicitaste, pero no sabrs nunca que la a a librer te ayud siguiendo el redireccionamiento necesario. Siempre seguirs usando a o a la vieja direccin, y cada vez que suceda, sers redirigido a la nueva direccin meo a o diante la ayuda que te presta el mdulo urllib.request. En otras palabras, trata las o redirecciones permanentes de la misma forma que las temporales. Esto signica que hacen falta dos vueltas en lugar de una para cada acceso, lo que es malo para el servidor y para ti. httplib2 maneja las redirecciones permanentes por ti. No solamente te dir que a ha sucedido una redireccin permanente, mantendr un registro local de estas redio a recciones y reescribir las URL afectadas antes de solicitarlas. a

14.3. COMO NO SE DEBE RECUPERAR INFORMACION A TRAVES DE HTTP309

14.3.

Cmo no se debe recuperar informacin a o o travs de HTTP e

Digamos que quieres descargar un recurso a travs de HTTP, por ejemplo, un e ujo de datos Atom. Como se trata de un ujo, no lo vas a descargar una unica vez; vas a descargarlo una y otra vez (La mayor de los lectores de noticias comprueban a si ha habido cambios una vez cada hora). Vamos a hacerlo de la forma ms manual a posible, en primer lugar, y luego veremos cmo hacerlo mejor. o
1 2 3 4 5 6 7 8 9 10 11 12 13 14 >>> import u r l l i b . r e q u e s t >>> a u r l = h t t p : / / d i v e i n t o p y t h o n 3 . o r g / examples / f e e d . xml >>> data = u r l l i b . r e q u e s t . u r l o p e n ( a u r l ) . r e a d ( ) >>> type ( data ) <c l a s s b y t e s > >>> print ( data ) <?xml v e r s i o n= 1 . 0 e n c o d i n g= u t f 8 ?> <f e e d xmlns= h t t p : / /www. w3 . o r g /2005/Atom xml : l a n g= en > < t i t l e >d i v e i n t o mark</ t i t l e > <s u b t i t l e >c u r r e n t l y between a d d i c t i o n s </ s u b t i t l e > <id>t a g : d i v e i n t o m a r k . org ,2001 07 29:/ </ id> <updated >2009 03 27T21 : 5 6 : 0 7 Z</updated> <l i n k r e l= a l t e r n a t e type= t e x t / html h r e f= h t t p : / / d i v e i n t o m a r k . o r g / /> ...

1. Lnea 3: La descarga de cualquier informacin a travs de HTTP es incre o e blemente sencilla en Python; de hecho se trata de una unica l nea. El mdulo o urllib.request dispone de una util funcin urlopen() que toma la direccin de la o o pgina que quieres y retorna un objeto de ujo (como un chero) que puedes a leer con la funcin read() para recuperar el contenido completo de la pgina. o a No puede ser ms sencillo. a 2. Lnea 4: El mtodo urlopen().read() siempre devuelve un objeto bytes, no una e cadena de texto. Recuerda que los bytes son bytes y los caracteres no son ms a que una abstraccin. Los servidores HTTP no se preocupan de la abstraccin. o o Si solicitas un recurso, obtienes bytes. Si quieres una cadena de texto, necesitars determinar la codicacin de caracteres utilizada para poder convertir a o los bytes en una cadena de texto. Qu tiene de mala esta forma de recuperar el recurso? Para una prueba rpida e a durante el desarrollo no hay nada de malo. Yo mismo lo hago todo el rato. Quer a el contenido de un ujo de noticias, y tengo el contenido de dicho ujo. La misma tcnica funciona con cualquier pgina web. Pero una vez comienzas a pensar en e a trminos de un servicio web al que quieres acceder frecuentemente (por ejemplo: e

310

CAP ITULO 14. SERVICIOS WEB HTTP

solicitar esta informacin una vez cada hora), entonces ests siendo ineciente y o a basto.

14.4.

Qu sucede por debajo? e

Para ver porqu esta manera de hacer la descarga es ineciente y basta, vamos e a activar las caracter sticas de depuracin de la librer de HTTP de Python para o a ver qu se est enviando a travs de la red. e a e
1 2 3 4 5 6 7 8 9 10 11 >>> from h t t p . c l i e n t import HTTPConnection >>> HTTPConnection . d e b u g l e v e l = 1 >>> from u r l l i b . r e q u e s t import u r l o p e n >>> r e s p o n s e = u r l o p e n ( h t t p : / / d i v e i n t o p y t h o n 3 . o r g / examples / f e e d . xml ) send : b GET / examples / f e e d . xml HTTP/ 1 . 1 Host : d i v e i n t o p y t h o n 3 . o r g Accept Encoding : i d e n t i t y User Agent : Python u r l l i b / 3 . 1 Connection : c l o s e r e p l y : HTTP/ 1 . 1 200 OK . . . s e omite e l r e s t o de l a d e p u r a c i n . . . o

1. Lnea 2: Como he comentado al comienzo del cap tulo, la librer urllib.request a se basa en otra librer estndar de Python, http.client. Normalmente no necesia a tas tocar directamente http.client (el mdulo urllib.request la importa automtio a camente). Pero la importamos aqu para modicar el control de depuracin de o la clase HTTPConnection que es la que utiliza urllib.request para conectarse al servidor HTTP. 2. Lnea 4: Ahora que se ha activado la depuracin, la informacin de la peticin o o o de de la respuesta HTTP se imprime en tiempo real. Como puedes ver, cuando solicitas el ujo Atom, el mdulo urllib.request env cinco l o a neas al servidor. 3. Lnea 5: La primera l nea especica el verbo HTTP que ests utilizando y el a camino al recurso (menos el nombre de dominio). 4. Lnea 6: La segunda l nea indica el nombre de dominio del que estamos solicitando este ujo. 5. L nea 7: La tercera l nea especica los algoritmos de compresin que el cliente o admite. Como he comentado antes, urllib.request no permite ningn tipo de u compresin por defecto. o

14.4. QUE SUCEDE POR DEBAJO?

311

6. Lnea 8: La cuarta l nea especica el nombre de la librer que est realizando a a la peticin. Por defecto, se muestra Pythonurllib ms el nmero de versin. o a u o Ambos mdulos urllib.request y httplib2 permiten la modicacin del agente de o o usuario, simplemente aadiendo la cabecera User-Agent a la peticin (lo que n o sustituir el valor por defecto). a Ahora vamos a ver lo que el servidor env de vuelta como respuesta. a
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # sigue d e l ejemplo a nt er io r >>> print ( r e s p o n s e . h e a d e r s . a s s t r i n g ( ) ) Date : Sun , 31 May 2009 1 9 : 2 3 : 0 6 GMT S e r v e r : Apache Last M o d i f i e d : Sun , 31 May 2009 0 6 : 3 9 : 5 5 GMT ETag : bfe 93d9c4c0 Accept Ranges : b y t e s Content Length : 3070 CacheC o n t r o l : maxage =86400 E x p i r e s : Mon, 01 Jun 2009 1 9 : 2 3 : 0 6 GMT Vary : Accept Encoding Connection : c l o s e Content Type : a p p l i c a t i o n /xml >>> data = r e s p o n s e . r e a d ( ) >>> l e n ( data ) 3070

1. Lnea 2: El objeto response devuelto por la funcin urllib.request.urlopen() con o tiene las cabeceras HTTP que el servidor ha devuelto. Tambin contiene mtoe e dos para descargar la informacin real devuelta; volveremos a ello en un mio nuto. 2. Lnea 3: El servidor te informa del momento en que proces tu peticin. o o 3. Lnea 5: La respuesta incluye una cabecera Last-Modied. 4. Lnea 6: Esta respuesta tambin incluye una cabecera ETag. e 5. Lnea 8: La informacin ocupa 3070 bytes. Observa lo que no aparece: una o cabecera Content-encoding. Tu peticin indic que solamente aceptas informao o cin sin comprimir (Accept-encoding: identity), y estamos seguros de que esta o respuesta solamente contiene informacin sin comprimir. o 6. Lnea 9: La respuesta incluye cabecers de cach que indican que este ujo se a e puede mantener en cach durante 24 horas (86400 segundos). e

312

CAP ITULO 14. SERVICIOS WEB HTTP

7. Lnea 14: Y nalmente, se descarga la informacin real mediante una llamada o a response.read(). Como puedes ver mediante el uso de la funcin len(), la o descarga es completa: los 3070 bytes de una vez. Como puedes ver, este cdigo es ineciente: solicita (y recibe) datos sin comprio mir. S con seguridad que este servidor soporta compresin gzip, pero la compresin e o o en HTTP es opcional. No lo pedimos en este caso, por lo que no la obtuvimos. Esto signica que estamos descargando 3070 bytes cuando podr amos haber descargado solamente 941. Te has portado mal, no hay premio. Pero espera, que es peor! Para ver lo ineciente que es este cdigo vamos a o pedir de nuevo el mismo recurso.
1 # sigue d e l ejemplo a nt er io r 2 >>> r e s p o n s e 2 = u r l o p e n ( h t t p : / / d i v e i n t o p y t h o n 3 . o r g / examples / f e e d . xml ) 3 send : b GET / examples / f e e d . xml HTTP/ 1 . 1 4 Host : d i v e i n t o p y t h o n 3 . o r g 5 Accept Encoding : i d e n t i t y 6 User Agent : Python u r l l i b / 3 . 1 7 Connection : c l o s e 8 r e p l y : HTTP/ 1 . 1 200 OK 9 . . . f u r t h e r debugging i n f o r m a t i o n o m i t t e d . . .

No observas nada raro en esta peticin? no ha cambiado! Es exactamente la o misma que la primera peticin. No existe seal de las cabeceras If-Modied-Since. o n No hay seal de cabeceras If-None-Match. No se respetan las cabeceras de cach y n e no hay compresin. o Y qu pasa cuando pides lo mismo dos veces? Pues que obtienes la misma e respuesta dos veces!

14.5. INTRODUCCION A HTTPLIB2


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # sigue d e l ejemplo a nt er io r >>> print ( r e s p o n s e 2 . h e a d e r s . a s s t r i n g ( ) ) Date : Mon, 01 Jun 2009 0 3 : 5 8 : 0 0 GMT S e r v e r : Apache Last M o d i f i e d : Sun , 31 May 2009 2 2 : 5 1 : 1 1 GMT ETag : bfe 255 e f 5 c 0 Accept Ranges : b y t e s Content Length : 3070 CacheC o n t r o l : maxage =86400 E x p i r e s : Tue , 02 Jun 2009 0 3 : 5 8 : 0 0 GMT Vary : Accept Encoding Connection : c l o s e Content Type : a p p l i c a t i o n /xml >>> data2 = r e s p o n s e 2 . r e a d ( ) >>> l e n ( data2 ) 3070 >>> data2 == data True

313

1. Lnea 2: El servidor env de nuevo la misma lista de cabeceras inteligentes: a Cache-Control y Expires para permitir el uso de una cach, Last-Modied y e ETag para facilitar el seguimiento de una modicacin de la pgina. Incluso la o a cabecera Vary: Accept-Encoding que informa de que el servidor podr soportar a compresin si se lo hubieras pedido. Pero no lo hiciste. o 2. Lnea 15: De nuevo, la recuperacin de esta informacin descarga los 3070 o o bytes... 3. Lnea 17: ...exactamente los mismos 3070 bytes que descargaste la ultima vez. HTTP est diseado para funcionar mejor que esto, urllib habla HTTP como a n yo hablo espaol lo suciente para integrarme en una esta, pero no lo sucenn te como para mantener una conversacin. HTTP es una conversacin. Es hora de o o actualizarnos a una librer que hable HTTP de forma uida. a

14.5.

Introduccin a httplib2 o

Antes de que puedas utilizar httplib2 necesitars instalarla. Visita http://code.google.com/p/h a y descarga la ultima versin. httplib2 est disponible para Python 2.x y para Python o a 3.x; asegrate de que descargas la versin de Python 3, denominada algo as como u o httplib2-python3-0.5.0.zip.

314

CAP ITULO 14. SERVICIOS WEB HTTP

Descomprime el archivo, abre el terminal en una ventana y vete al directorio recin creado httplib2. En Windows abre el men Inicio, selecciona Ejecutar..., teclea e u cmd.exe y pulsa INTRO.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 c : \ U s e r s \ p i l g r i m \ Downloads> d i r Volume in d r i v e C has no l a b e l . Volume S e r i a l Number i s DED5B4F8 D i r e c t o r y o f c : \ U s e r s \ p i l g r i m \ Downloads 07/28/2009 07/28/2009 07/28/2009 07/28/2009 1 2 : 3 6 PM <DIR> . 1 2 : 3 6 PM <DIR> .. 1 2 : 3 6 PM <DIR> h t t p l i b 2 python3 0 . 5 . 0 1 2 : 3 3 PM 1 8 , 9 9 7 h t t p l i b 2 python3 0 . 5 . 0 . z i p 1 File ( s ) 18 ,997 bytes 3 Dir ( s ) 6 1 , 4 9 6 , 6 8 4 , 5 4 4 b y t e s f r e e

c : \ U s e r s \ p i l g r i m \ Downloads> cd h t t p l i b 2 python3 0 . 5 . 0 c : \ U s e r s \ p i l g r i m \ Downloads \ h t t p l i b 2 python3 0.5.0 > c : \ python31 \ python . exe s e t u p . py i n s t a l l running i n s t a l l running build running build py running i n s t a l l l i b c r e a t i n g c : \ python31 \ Lib \ s i t e p a c k a g e s \ h t t p l i b 2 c o p y i n g b u i l d \ l i b \ h t t p l i b 2 \ i r i 2 u r i . py > c : \ python31 \ Lib \ s i t e p a c k a g e s \ h t t p l i b 2 copying build \ l i b \ h t t p l i b 2 \ i n i t . py > c : \ python31 \ Lib \ s i t e p a c k a g e s \ h t t p l i b 2 byte c o m p i l i n g c : \ python31 \ Lib \ s i t e p a c k a g e s \ h t t p l i b 2 \ i r i 2 u r i . py t o i r i 2 u r i . pyc byte c o m p i l i n g c : \ python31 \ Lib \ s i t e p a c k a g e s \ h t t p l i b 2 \ i n i t . py t o init . pyc running i n s t a l l e g g i n f o Writing c : \ python31 \ Lib \ s i t e p a c k a g e s \ h t t p l i b 2 python3 0 .5.0 py3 . 1 . egg i n f o

En Mac OS X ejecuta la aplicacin Terminal.app de la carpeta /Aplicaciones/Uo tilidades. En Linux, ejecuta la aplicacin de Terminal, que normalmente se encuentra o en el menu de Aplicaciones bajo Accesorios o Sistema.

14.5. INTRODUCCION A HTTPLIB2


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

315

y o u @ l o c a l h o s t : / Desktop$ u n z i p h t t p l i b 2 python3 0 . 5 . 0 . z i p A r c h i v e : h t t p l i b 2 python3 0 . 5 . 0 . z i p i n f l a t i n g : h t t p l i b 2 python3 0 . 5 . 0 /README i n f l a t i n g : h t t p l i b 2 python3 0 . 5 . 0 / s e t u p . py i n f l a t i n g : h t t p l i b 2 python3 0 . 5 . 0 /PKG INFO i n f l a t i n g : h t t p l i b 2 python3 0 . 5 . 0 / h t t p l i b 2 / i n i t . py i n f l a t i n g : h t t p l i b 2 python3 0 . 5 . 0 / h t t p l i b 2 / i r i 2 u r i . py y o u @ l o c a l h o s t : / Desktop$ cd h t t p l i b 2 python3 0 . 5 . 0 / y o u @ l o c a l h o s t : / Desktop / h t t p l i b 2 python3 0 . 5 . 0 $ sudo python3 s e t u p . py i n s t a l l running i n s t a l l running build running build py creating build c r e a t i n g b u i l d / l i b . l i n u x x86 64 3.1 c r e a t i n g b u i l d / l i b . l i n u x x86 64 3.1/ h t t p l i b 2 c o p y i n g h t t p l i b 2 / i r i 2 u r i . py > b u i l d / l i b . l i n u x x86 64 3.1/ h t t p l i b 2 copying h t t p l i b 2 / i n i t . py > b u i l d / l i b . l i n u x x86 64 3.1/ h t t p l i b 2 running i n s t a l l l i b c r e a t i n g / u s r / l o c a l / l i b / python3 . 1 / d i s t p a c k a g e s / h t t p l i b 2 c o p y i n g b u i l d / l i b . l i n u x x86 64 3.1/ h t t p l i b 2 / i r i 2 u r i . py > / u s r / l o c a l / l i b / python3 . 1 / d i s t p a c k a g e s / h t t p l i b 2 c o p y i n g b u i l d / l i b . l i n u x x86 64 3.1/ h t t p l i b 2 / i n i t . py > / u s r / l o c a l / l i b / python3 . 1 / d i s t p a c k a g e s / h t t p l i b 2 byte c o m p i l i n g / u s r / l o c a l / l i b / python3 . 1 / d i s t p a c k a g e s / h t t p l i b 2 / i r i 2 u r i . py t o i r i 2 u r i . pyc byte c o m p i l i n g / u s r / l o c a l / l i b / python3 . 1 / d i s t p a c k a g e s / h t t p l i b 2 / i n i t . py to init . pyc running i n s t a l l e g g i n f o Writing / u s r / l o c a l / l i b / python3 . 1 / d i s t p a c k a g e s / h t t p l i b 2 python3 0 . 5 . 0 . egg i n f o

Para utilizar httplib2 crea una instancia de la clase httplib2.Http.


1 2 3 4 5 6 7 8 9 10 >>> import h t t p l i b 2 >>> h = h t t p l i b 2 . Http ( . c a c h e ) >>> r e s p o n s e , c o n t e n t = h . r e q u e s t ( h t t p : / / d i v e i n t o p y t h o n 3 . o r g / examples / f e e d . xml ) >>> r e s p o n s e . s t a t u s 200 >>> c o n t e n t [ : 5 2 ] b<?xml v e r s i o n = 1 . 0 e n c o d i n g = u t f 8?>\ r \n<f e e d xmlns= >>> l e n ( c o n t e n t ) 3070

1. Lnea 2: El interfaz principal de httplib2 es el objeto Http. Por razones que vers en la siguiente seccin, siempre deber pasar el nombre de un directorio a o as

316

CAP ITULO 14. SERVICIOS WEB HTTP al crear el objeto Http. No es necesario que el directorio exista, httplib2 lo crear si es necesario. a

2. Lnea 3: Una vez has creado el objeto Http, la recuperacin de los datos es o tan simple como llamar al mtodo request() con la direccin de los datos que e o necesitas. Esto emitir una peticin HTTP GET para la URL deseada (Ms a o a adelante, en este cap tulo, te mostrar cmo solicitar otro tipo de peticiones e o HTTP, como POST). 3. L nea 4: El mtodo request() retorna dos valores. El primero es un objeto htte plib2.Response, que contiene todas las cabeceras que retorna el servidor. Por ejemplo, un cdigo de status 200 indica que la peticin se complet satisfactoo o o riamente. 4. Lnea 6: La variable content contiene la informacin real que se retorn desde o o el servidor HTTP. La informacin se devuelve como un objeto bytes, no una o cadena. Si quieres que sea una cadena, necesitas determinar la codicacin de o caracteres y convertirla t mismo. u Probablemente necesites un unico objeto httplib2.Http. No obstante, exis ten razones vlidas por las que puedas necesitar ms de uno, pero dea a ber crearlos unicamente si conoces porqu los necesitas. Necesito as e datos desde dos URL diferentes no es una razn vlida; en su lugar, o a reutilizar el objeto Http y llama dos veces al mtodo request(). e

14.5.1.

Una breve disgresin para explicar porqu httplib2 o e devuelve Bytes en lugar de cadenas de texto

Bytes, cadenas de texto, qu cansancio! Porqu httplib2 no hace simplemente e e la conversin por ti? Bueno, es complejo, porque las reglas para determinar la codio cacin de caracteres son espec o cas del tipo de recurso que ests solicitano. Cmo e o podr httplib2 conocer la clase de recurso que ests solicitando? Normalmente se a a encuentra en la cabecera Content-Type HTTP, pero se trata de una caracter stica opcional de HTTP y no todos los servidores HTTP la incluyen. Si esa cabecera no est incluida en la respuesta HTTP, es el cliente el que tiene que adivinarlo (A esto a se le suele llamar inspeccin del contenido, y no es una solucin perfecta). o o Si supieras que clase de recursos ests esperando (un documento XML en este a caso), tal vez podr simplemente pasar el objeto bytes a la funcin xml.etree.ElementTree.parse(). as o Eso funcionar siempre que el documento XML incluyera la informacin de su proa o pia codicacin de caracteres (como hace en este caso), pero eso es una caracter o stica

14.5. INTRODUCCION A HTTPLIB2

317

opcional y no todos los documentos XML lo indican. Si un documento XML no incluye la informacin de codicacin de caracteres, se supone que el cliente tiene que o o mirar en el protocolo de transporte en este caso la cabecera Content-Type HTTP, que puede incluir el parmetro charset. a Pero es an peor. Ahora la informacin sobre la codicacin de caracteres u o o puede encontrarse en dos lugares: dentro del propio documento XML y dentro de la cabecera Content-Type HTTP. Si la informacin est en ambos lugares... cul o a a gana? De acuerdo a la especiacin RFC3023http://www.ietf.org/rfc/rfc3023.txt (te o lo juro, no me lo estoy inventando), si el tipo de medio indicado en la cabecera Content-Type HTTP es application/xml, application/xml-dtd, application/xml-externalparsed-entity o cualquier otro subtipo de application/xml como application/atom+xml o incluso application/rdf+xml, entonces la codicacin de caracteres es: o 1. la codicacin dada en el parmetro charset de la cabecera Content-Type HTTP o a o 2. la codicacin dada en el atributo encoding de la declaracin XML dentro del o o documento o 3. UTF-8 Por otra parte, si el tipo de medio dado en la cabecera Content-Type HTTP es text/xml, text/xml-external-parsed-entity o un subtipo como text/CualquierCosa+xml, entonces el atributo encoding de la declaracin dentro del documento XML se ignora o totalmente y la codicacin es: o 1. la indicada en el parmetro charset de la cabecera Content-Type HTTP o a 2. us-ascii Y eso unicamente para los documentos XML. Para los documentos HTML los navegadores han construido unas reglas tan bizantinas para identicacin del conteo nidohttp://www.adambarth.com/papers/2009/barth-caballero-song.pdf que an esu tamos intentando aclarar las que sonhttp://www.google.com/search?q=barth+contenttype+processing+model.

14.5.2.

Cmo httplib2 gestiona la cach o e

Recuerdas cuando en la seccin anterior te dije que deber crear siempre el o as objeto httplib2.Http con un nombre de directorio? La razn es la cach. o e

318
1 2 3 4 5 6 7 8 9

CAP ITULO 14. SERVICIOS WEB HTTP

# sigue d e l ejemplo a nt er io r >>> r e s p o n s e 2 , c o n t e n t 2 = h . r e q u e s t ( h t t p : / / d i v e i n t o p y t h o n 3 . o r g / examples / f e e d . xml ) >>> r e s p o n s e 2 . s t a t u s 200 >>> c o n t e n t 2 [ : 5 2 ] b<?xml v e r s i o n = 1 . 0 e n c o d i n g = u t f 8?>\ r \n<f e e d xmlns= >>> l e n ( c o n t e n t 2 ) 3070

1. Lnea 2: No deber sorprenderte, es lo mismo que ya hemos hecho antes, as excepto que el resultado lo estamos guardando en dos variables nuevas. 2. Lnea 3: El HTTP status vuelve a ser 200, como antes. 3. Lnea 5: El contenido descargado tambin es el mismo que la ultima vez. e Pero A qu viene esto? Sal de la consola de Python y vuelve a relanzarla en e una nueva sesin y te lo mostrar: o e
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # NO s i g u e d e l e j e m p l o a n t e r i o r # Por f a v o r , s a l a de l a c o n s o l a de Python # y v u e l v e a e n t r a r en una nueva >>> import h t t p l i b 2 >>> h t t p l i b 2 . d e b u g l e v e l = 1 >>> h = h t t p l i b 2 . Http ( . c a c h e ) >>> r e s p o n s e , c o n t e n t = h . r e q u e s t ( h t t p : / / d i v e i n t o p y t h o n 3 . o r g / examples / f e e d . xml ) >>> l e n ( c o n t e n t ) 3070 >>> r e s p o n s e . s t a t u s 200 >>> r e s p o n s e . fromcache True

1. Lnea 5: Activamos la depuracin pa ver lo que est sucediendo en la comuni o a cacin. Esta es la forma en la que httlib2 activa la depuracin (como hac o o amos antes con http.client). En este cao httplib2 imprimir todos los datos que se a env al servidor e informacin clave que se retorna desde el mismo. an o 2. Lnea 6: Se crea un objeto httplib2.Http con el mismo nombre de directorio que antes. 3. Lnea 7: Se solicita la misma URL que antes. Parece que no pasa nada. De forma ms precisa, nada se env al servidor, y nada se devuelve del servidor. a a No hay ninguna actividad de red.

14.5. INTRODUCCION A HTTPLIB2

319

4. Lnea 8: Pero s que recibimos datos de hecho, hemos recibido toda la in formacin. o 5. Lnea 10: Tambin hemos recibido el cdigo de estado HTTP 200 indicando e o que la peticin fue satisfactoria. o 6. Lnea 12: Este es el secreto: Esta respuesta se ha generado desde la cach lo e cal de httplib2. El directorio que le has pasado al rear el objeto httplib2.Http contiene la cach de httplib2 de todas las operaciones que se han ejecutado. e Si quieres activar la depuracin httlib2, necesitas activar una constante o al nivel del mdulo (httplib2.debuglevel) y luego crear un objeto nuevo o httplib2.Http. Si quieres desactivar la depuracin, necesitas modicar la o misma constante y luego crear otro nuevo objeto httplib2.Http. Anteriormente solicitaste informacin de esta URL. Las peticiones fueron sao tisfactorias (status: 200). Esta respuesta incluye no solamente los datos reales, sino tambin las cabeceras de cach que indican a quien recuper los datos que los puede e e o mantener en cach durante 24 horas (Cache-Control: max-age=86400, que son 24 hoe ras medidas en segundos). httlib2 comprende y respeta las cabeceras de cach y almae cena la respuesta anterior en el directorio .cache (que hemos pasado como parmetro a al crear el objeto Http). Esta cach an no ha expirado, por lo que la segunda vez e u que se solicita la informacin de esta URL httplib2 devuelve el resultado que tiene o en la cach sin salir a la red a buscarlo. e Obviamente, esta simplicidad esconde la complejidad que supone esto: httplib2 maneja el cacheo de HTTP de forma automtica y por defecto. Si por alguna razn a o necesitases conocer si la respuesta vino de la cach, puedes comprobar el valor de e response.fromcache. Ahora supn que tienes datos en la cach, pero quieres saltarte la cach y o e e solicitar de nuevo los datos del servidor remoto. Los navegadores hacen esto si el usuario lo solicita espec camente. Por ejemplo, al pulsar F5 se refresca la pgina a actual, pero al pulsar Ctrl-F5 se salta la cach y vuelve a consultar la pgina al e a servidor remoto. Podr pensar bastar con borrar la cach local o volver a conas a e sultar la pgina actual al servidor remoto. Podr hacer esto, pero recuerda que a as hay terceros involucrados en la consulta, no solamente t y el servidor. Qu pasa u e con los servidores proxy intermedios? Estn completamente fuera de tu control y a pueden tener an datos en sus cachs. Datos que te devolvern porque para ellos la u e a cach an es vlida. e u a En lugar de manipular la cach local y esperar que haya suerte, deber utilizar e as las caracter sticas que dene el protocolo HTTP para asegurarte de que tu consulta realmente alcanza al servidor remoto.

320
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

CAP ITULO 14. SERVICIOS WEB HTTP

# sigue d e l ejemplo a nt er io r >>> r e s p o n s e 2 , c o n t e n t 2 = h . r e q u e s t ( h t t p : / / d i v e i n t o p y t h o n 3 . o r g / examples / f e e d . xml , ... h e a d e r s={ cache c o n t r o l : noc a c h e } ) c o n n e c t : ( d i v e i n t o p y t h o n 3 . org , 8 0 ) send : b GET / examples / f e e d . xml HTTP/ 1 . 1 Host : d i v e i n t o p y t h o n 3 . o r g u s e r a g e n t : Python h t t p l i b 2 /Rev : 259 a c c e p t e n c o d i n g : d e f l a t e , g z i p cache c o n t r o l : noc a c h e r e p l y : HTTP/ 1 . 1 200 OK . . . f u r t h e r debugging i n f o r m a t i o n o m i t t e d . . . >>> r e s p o n s e 2 . s t a t u s 200 >>> r e s p o n s e 2 . fromcache False >>> print ( d i c t ( r e s p o n s e 2 . i t e m s ( ) ) ) { s t a t u s : 200 , c o n t e n t l e n g t h : 3070 , c o n t e n t l o c a t i o n : h t t p : / / d i v e i n t o p y t h o n 3 . o r g / examples / f e e d . xml , a c c e p t r a n g e s : b y t e s , e x p i r e s : Wed, 03 Jun 2009 0 0 : 4 0 : 2 6 GMT , vary : Accept Encoding , s e r v e r : Apache , l a s t m o d i f i e d : Sun , 31 May 2009 2 2 : 5 1 : 1 1 GMT , connection : close , c o n t e n t e n c o d i n g : g z i p , e t a g : bfe 255 e f 5 c 0 , cache c o n t r o l : maxage =86400 , d a t e : Tue , 02 Jun 2009 0 0 : 4 0 : 2 6 GMT , c o n t e n t type : a p p l i c a t i o n /xml }

1. Lnea 4: httplib2 te permite aadir cabeceras HTTP a cualquier peticin sa n o liente. Para poder saltarnos todas las cachs (no unicamente tu cach local, e e sino todas las cachs de los proxys entre el servidor remoto y t), aade la e u n cabecera no-cache en el diccionario headers. 2. Lnea 5: Ahora se observa que httplib2 inicia una conexin a la red. httplib2 o comprende y respeta las cabeceras de cach en ambas direcciones como parte e de la respuesta y como parte de la peticin de salida. Detecta que has aadido o n una cabecera no-cache por lo que se salta la cach local y no tiene otra eleccin e o que salir a la red a solicitar la informacin. o 3. Lnea 15: Esta respuesta no se gener de la cach local. Ya lo sab viste la o e as, informacin de depuracin de la peticin de salida. Pero es bueno disponer de o o o un modo de vericarlo desde el programa.

14.5. INTRODUCCION A HTTPLIB2

321

4. Lnea 17: La peticin termin satisfactoriamente; descargaste la informacin o o o completa de nuevo desde el servidor remoto. Desde luego, el servidor volvi a o enviar toda la informacin de cabeceras junto con los datos. Incluye las cao beceras de cach, que httplib2 utilizar para actualizar su cach local, con la e a e esperanza de evitar el acceso a la red la siguiente vez que solicites esta informacin. El cacheo HTTP est diseado para maximizar el uso de la cach y o a n e minimizar el acceso a la red. Incluso aunque te hayas saltado la cach esta vez, e el servidor remoto apreciar que te guardases el resultado en la cach para la a e prxima vez. o

14.5.3.

Cmo httplib2 gestiona las cabeceras Last-Modied y o ETag

Las cabeceras de cach Cache-Control y Expires se suelen denominar indicadores e de frescura. Indican al sistema de cach de modo totalmente seguro que se puede e evitar totalmente el acceso a la red hasta que expira el plazo. Y se ha sido el e comportamiento que has visto funcionando en la seccin anterior: dado un indicador o de frescura, httplib2 no genera ningn byte de actividad en la red para mostrar u informacin que se encuentre en la cach (a menos que t lo indiques expresamente, o e u desde luego). Pero qu sucede cuado los datos podr haber cambiado. HTTP defne unas e an cabeceras Last-Modied y Etag para este n. Estas cabeceras se denominan validadores. Si la cach local ya est desactualizada, el cliente puede enviar los validadores e a con la siguiente consulta, para ver si los datos han cambiado realmente. Si los datos no han cambiado, el servidor devuelve un cdigo de estado 304 y ningn dato ms. o u a Por lo que aunque hay una consulta a la red, se descargan menos bytes.

322
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

CAP ITULO 14. SERVICIOS WEB HTTP

>>> import h t t p l i b 2 >>> h t t p l i b 2 . d e b u g l e v e l = 1 >>> h = h t t p l i b 2 . Http ( . c a c h e ) >>> r e s p o n s e , c o n t e n t = h . r e q u e s t ( h t t p : / / d i v e i n t o p y t h o n 3 . o r g / ) c o n n e c t : ( d i v e i n t o p y t h o n 3 . org , 8 0 ) send : b GET / HTTP/ 1 . 1 Host : d i v e i n t o p y t h o n 3 . o r g a c c e p t e n c o d i n g : d e f l a t e , g z i p u s e r a g e n t : Python h t t p l i b 2 /$Rev : 259 $ r e p l y : HTTP/ 1 . 1 200 OK >>> print ( d i c t ( r e s p o n s e . i t e m s ( ) ) ) { c o n t e n t e n c o d i n g : g z i p , a c c e p t r a n g e s : b y t e s , connection : close , c o n t e n t l e n g t h : 6657 , c o n t e n t l o c a t i o n : h t t p : / / d i v e i n t o p y t h o n 3 . o r g / , c o n t e n t type : t e x t / html , d a t e : Tue , 02 Jun 2009 0 3 : 2 6 : 5 4 GMT , e t a g : 7 f806d 1a01 9f b 9 7 9 0 0 , l a s t m o d i f i e d : Tue , 02 Jun 2009 0 2 : 5 1 : 4 8 GMT , s e r v e r : Apache , s t a t u s : 200 , vary : Accept Encoding , User Agent } >>> l e n ( c o n t e n t ) 6657

1. Lnea 4: En lugar del uo, esta vez vamos a descargar la pgina de inicio de a la sede web, que es HTML. 2. Lnea 11: La respuesta incluye muchas cabeceras HTTP... pero no incluye informacin de cach. Sin embargo, s incluye cabeceras ETag y Last-Modied. o e 3. Lnea 24: En el momento en que se incluy este ejemplo, esta pgina era o a de 6657 bytes. Probablemente haya cambiado desde entonces, pero eso no es relevante.

14.5. INTRODUCCION A HTTPLIB2


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # sigue d e l ejemplo a nt er io r >>> r e s p o n s e , c o n t e n t = h . r e q u e s t ( h t t p : / / d i v e i n t o p y t h o n 3 . o r g / ) c o n n e c t : ( d i v e i n t o p y t h o n 3 . org , 8 0 ) send : b GET / HTTP/ 1 . 1 Host : d i v e i n t o p y t h o n 3 . o r g i f nonematch : 7 f806d 1a01 9f b 9 7 9 0 0 i f m o d i f i e d s i n c e : Tue , 02 Jun 2009 0 2 : 5 1 : 4 8 GMT a c c e p t e n c o d i n g : d e f l a t e , g z i p u s e r a g e n t : Python h t t p l i b 2 /Rev : 259 r e p l y : HTTP/ 1 . 1 304 Not M o d i f i e d >>> r e s p o n s e . fromcache True >>> r e s p o n s e . s t a t u s 200 >>> r e s p o n s e . d i c t [ s t a t u s ] 304 >>> l e n ( c o n t e n t ) 6657

323

1. Lnea 2: Solicitas la misma pgina otra vez, con el mismo objeto Http (y la a misma cach local). e 2. Lnea 6: httplib2 env el valor de la etiqueta Etag al servidor en la cabecera a If-None-Match. 3. Lnea 7: httplib2 tambin env el valor de la etiqueta Last-Modied al servidor e a en la etiqueta If-Modied-Since. 4. Lnea 10: El servidor recupera estos valores (validadores), observa la pgina a que has solicitado y determina que la pgina no ha cambiado desde la ultima a vez que la solicitaste, por lo que devuelve un cdigo 304 y ningn dato real. o u 5. Lnea 11: De vuelta al cliente, httplib2 comprueba el cdigo de estado 304 y o carga el contenido de la pgina desde la cach. a e 6. Lnea 13: Esto puede que sea algo extrao. En realidad existen dos cdigos n o de estado 304 devuelto del servidor esta vez, que ha provocado que httplib2 recupere de la cach, y 200, devuelto del servidor la ultima vez que se recupee raron los datos, y que est almacenado en la cach de httplib2 junto con los a e datos. El atributo response.status obtiene el estado residente en la cach. e 7. Lnea 15: Si lo que se desea es ver el cdigo de estado actual del servidor o es necesario utilizar el diccionario response.dict, que mantiene las cabeceras recibidas en la consulta actual al servidor.

324

CAP ITULO 14. SERVICIOS WEB HTTP

8. Lnea 17: Sin embargo, an tienes el tamao de los datos en la variable content. u n Generalmente, no necestias conocer porqu una respuesta ha sido obtenida e de la cach. Puede que ni te interese conocer si fue servida o no desde la e cach. Cuando el mtodo request() naliza, httplib2 ya ha actualizado la cach, e e e devolvindote los datos que estn en ella. e a

14.5.4.

Cmo maneja la compresin httplib2 o o

HTTP permite varios tipos de compresin; los dos ms comunes son gzip y o a deate. httplib2 permite ambos tipos.
1 >>> r e s p o n s e , c o n t e n t = h . r e q u e s t ( h t t p : / / d i v e i n t o p y t h o n 3 . o r g / ) 2 c o n n e c t : ( d i v e i n t o p y t h o n 3 . org , 8 0 ) 3 send : b GET / HTTP/ 1 . 1 4 Host : d i v e i n t o p y t h o n 3 . o r g 5 a c c e p t e n c o d i n g : d e f l a t e , g z i p 6 u s e r a g e n t : Python h t t p l i b 2 /Rev : 259 7 r e p l y : HTTP/ 1 . 1 200 OK 8 >>> print ( d i c t ( r e s p o n s e . i t e m s ( ) ) ) 9 { c o n t e n t e n c o d i n g : g z i p , 10 a c c e p t r a n g e s : b y t e s , 11 connection : close , 12 c o n t e n t l e n g t h : 6657 , 13 c o n t e n t l o c a t i o n : h t t p : / / d i v e i n t o p y t h o n 3 . o r g / , 14 c o n t e n t type : t e x t / html , 15 d a t e : Tue , 02 Jun 2009 0 3 : 2 6 : 5 4 GMT , 16 e t a g : 7 f806d 1a01 9f b 9 7 9 0 0 , 17 l a s t m o d i f i e d : Tue , 02 Jun 2009 0 2 : 5 1 : 4 8 GMT , 18 s e r v e r : Apache , 19 s t a t u s : 304 , 20 vary : Accept Encoding , User Agent }

1. Lnea 5: Cada vez que httplib2 env una peticin, incluye una cabecera Accept a o Encoding para indicarle al servidor que puede manipular compresiones gzip o deate. 2. Lnea 9: En este caso, el servidor ha respondido descargando la informacin o comprimida con formato gzip. En el momento en que request() naliza, httplib2 ya ha descomprimido el cuerpo de la respuesta y lo ha colocado en la variable content. Si tienes curiosidad de conocer si la respuesta original estaba o no codicada puedes consultar response[-content-encoding].

14.5. INTRODUCCION A HTTPLIB2

325

14.5.5.

Cmo maneja las redirecciones httplib2 o

HTTP dene dos tipos de redirecciones: temporales y permanentes. No hay nada especial a hacer en las redirecciones temporales, excepto seguirlas, lo que httplib2 hace automticamente. a
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 >>> >>> >>> >>> import h t t p l i b 2 httplib2 . debuglevel = 1 h = h t t p l i b 2 . Http ( . c a c h e ) response , content = h . request ( h t t p : / / d i v e i n t o p y t h o n 3 . o r g / examples / f e e d 302. xml ) c o n n e c t : ( d i v e i n t o p y t h o n 3 . org , 8 0 ) send : b GET / examples / f e e d 302. xml HTTP/ 1 . 1 Host : d i v e i n t o p y t h o n 3 . o r g a c c e p t e n c o d i n g : d e f l a t e , g z i p u s e r a g e n t : Python h t t p l i b 2 /Rev : 259 r e p l y : HTTP/ 1 . 1 302 Found send : b GET / examples / f e e d . xml HTTP/ 1 . 1 Host : d i v e i n t o p y t h o n 3 . o r g a c c e p t e n c o d i n g : d e f l a t e , g z i p u s e r a g e n t : Python h t t p l i b 2 /Rev : 259 r e p l y : HTTP/ 1 . 1 200 OK

1. Lnea 5: No existe ningn ujo de datos en esta URL. He congurado mi u servidor para enviar una redireccin temporal a la direccin correcta. o o 2. Lnea 7: Esta es la peticin. o 3. Lnea 11: Y esta es la respuesta 302 Found. No se muestra aqu esta respuesta , tambin incluye una cabecera Location que apunta a la direccin URL real. e o 4. Lnea 12: httplib2 se dirige a la nueva direccin mediante una nueva peticin a o o la URL indicada en la cabecera Location: http://diveintopython3.org/examples/feed.xml La redireccin no es ms que lo se ha enseado en este ejemplo. httlib2 env o a n a una peticin a la URL que le indicaste. El servidor devuelve una respuesta que dice o No, no, en vez de aqu mira en este otro sitio. httlib2 env una nueva peticin , a o automticamente a la nueva URL. a

326

CAP ITULO 14. SERVICIOS WEB HTTP

1 # sigue d e l ejemplo a nt er io r 2 >>> r e s p o n s e 3 { s t a t u s : 200 , 4 c o n t e n t l e n g t h : 3070 , 5 c o n t e n t l o c a t i o n : h t t p : / / d i v e i n t o p y t h o n 3 . o r g / examples / f e e d . xml , 6 a c c e p t r a n g e s : b y t e s , 7 e x p i r e s : Thu , 04 Jun 2009 0 2 : 2 1 : 4 1 GMT , 8 vary : Accept Encoding , 9 s e r v e r : Apache , 10 l a s t m o d i f i e d : Wed, 03 Jun 2009 0 2 : 2 0 : 1 5 GMT , 11 connection : close , 12 c o n t e n t e n c o d i n g : g z i p , 13 e t a g : bfe 4c b b f 5 c 0 , 14 cache c o n t r o l : maxage =86400 , 15 d a t e : Wed, 03 Jun 2009 0 2 : 2 1 : 4 1 GMT , 16 c o n t e n t type : a p p l i c a t i o n /xml }

1. Lnea 2: La respuesta que se obtiene en esta llamada, response del mtodo e request() es la respuesta de la URL nal. 2. Lnea 5: httplib2 aade la URL nal al diccionario response como una cabecera n content-location. No es una cabecera que viniera del servidor, es espec ca de httplib2. 3. Lnea 12: Por cierto, este ujo est comprimido. a 4. Lnea 14: Y se puede guardar en la cach (Esto es importante, como vers en e a un minuto). La respuesta que obtienes (response) te informa sobre la URL nal. Qu hay e que hacer si quieres ms informacin sobre las URLs intermedias, las que te llevaron a o a la ultima? httplib2 te lo permite.

14.5. INTRODUCCION A HTTPLIB2


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

327

# sigue d e l ejemplo a nt er io r >>> r e s p o n s e . p r e v i o u s { s t a t u s : 302 , c o n t e n t l e n g t h : 228 , c o n t e n t l o c a t i o n : h t t p : / / d i v e i n t o p y t h o n 3 . o r g / examples / f e e d 302. xml , e x p i r e s : Thu , 04 Jun 2009 0 2 : 2 1 : 4 1 GMT , s e r v e r : Apache , connection : close , l o c a t i o n : h t t p : / / d i v e i n t o p y t h o n 3 . o r g / examples / f e e d . xml , cache c o n t r o l : maxage =86400 , d a t e : Wed, 03 Jun 2009 0 2 : 2 1 : 4 1 GMT , c o n t e n t type : t e x t / html ; c h a r s e t=i s o 8859 1 } >>> type ( r e s p o n s e ) <c l a s s h t t p l i b 2 . Response > >>> type ( r e s p o n s e . p r e v i o u s ) <c l a s s h t t p l i b 2 . Response > >>> r e s p o n s e . p r e v i o u s . p r e v i o u s >>>

1. Lnea 2: El atributo response.previous almacena una referencia al objeto res ponse seguido anteriormente a que httplib2 obtuviera la respuesta actual. 2. Lnea 13: Ambos objetos, response y response.previous, son del tipo httplib2.Response. 3. Lnea 17: Esto signica que puedes hacer response.previous.previous para seguir la cadena de redireccionamiento ms atrs en el tiempo. El escenario podr a a a ser: una URL redirige a una segunda URL que a su vez redirige a una tercera podr ser! En este ejemplo ya hemos alcanzado el comienzo de la cadena de a redireccionamiento por lo que el atributo vale None. Qu sucede si vuelves a pedir la misma URL? e
1 # sigue d e l ejemplo a nt er io r 2 >>> r e s p o n s e 2 , c o n t e n t 2 = h . r e q u e s t ( 3 h t t p : / / d i v e i n t o p y t h o n 3 . o r g / examples / f e e d 302. xml ) 4 c o n n e c t : ( d i v e i n t o p y t h o n 3 . org , 8 0 ) 5 send : b GET / examples / f e e d 302. xml HTTP/ 1 . 1 6 Host : d i v e i n t o p y t h o n 3 . o r g 7 a c c e p t e n c o d i n g : d e f l a t e , g z i p 8 u s e r a g e n t : Python h t t p l i b 2 /Rev : 259 9 r e p l y : HTTP/ 1 . 1 302 Found 10 >>> c o n t e n t 2 == c o n t e n t 11 True

1. Lnea 3: Misma URL, mismo httplib.Http (y, por lo tanto, misma cach). e

328

CAP ITULO 14. SERVICIOS WEB HTTP

2. Lnea 5: La respuesta 302 no se guard en la cach, por eso httplib2 vuelve a o e enviar una peticin por la misma URL. o 3. Lnea 9: De nuevo, el servidor responde con un cdigo de estado 302. Pe o ro observa lo que no ocurri: No hay una segunda peticin a la URL nal, o o http://diveintopython.org/examples/feed.xml. Esta URL est en la cach (rea e cuerda la cabecera Cache-Contro que viste en el ejemplo anterior. Una vez httplib2 recibe el cdigo 302 Found, comprueba la cach antes de pedir otra vez la o e pgina. La cach contiene una copia vigente de http://diveintopython3.org/examples/feed.xml, a e por lo que no hay ninguna necesidad de volver a pedirla. 4. Lnea 10: Al nalizar el mtodo request() ha le los datos de la cach y los e do e ha devuelto. Por supuesto, son los mismos datos que recibiste la vez anterior. En otras palabras, no tienes que hacer nada especial para el redireccionamiento temporal. httplib2 los sigue de forma automtica, y el hecho de que una URL a redirija a otra no afecta ni al soporte de compresin, cach, ETags o cualquier otra o e caracter stica de HTTP. El redireccionamiento permanente es igual de simple.
1 # sigue d e l ejemplo a nt er io r 2 >>> r e s p o n s e , c o n t e n t = h . r e q u e s t ( 3 h t t p : / / d i v e i n t o p y t h o n 3 . o r g / examples / f e e d 301. xml ) 4 c o n n e c t : ( d i v e i n t o p y t h o n 3 . org , 8 0 ) 5 send : b GET / examples / f e e d 301. xml HTTP/ 1 . 1 6 Host : d i v e i n t o p y t h o n 3 . o r g 7 a c c e p t e n c o d i n g : d e f l a t e , g z i p 8 u s e r a g e n t : Python h t t p l i b 2 /Rev : 259 9 r e p l y : HTTP/ 1 . 1 301 Moved Permanently 10 >>> r e s p o n s e . fromcache 11 True

1. Lnea 3: De nuevo, esta URL no existe en realidad. He congurado mi servidor para que retorne una redireccin permanente a http://diveintopython3.org/examples/feed.xml. o 2. Lnea 9: Aqu est: el cdigo de estado 301. Pero de nuevo, observa lo que no a o ha sucedido: no hay una nueva peticin a la URL nueva. Por qu? porque ya o e est en la cach. a e 3. Lnea 10: httplib2 sigui la redireccin desde la cach. o o e Pero espera, que hay ms! a

14.6. UN PASO MAS ALLA DE HTTP GET


1 2 3 4 5 6 7 # sigue d e l ejemplo a nt er io r >>> r e s p o n s e 2 , c o n t e n t 2 = h . r e q u e s t ( h t t p : / / d i v e i n t o p y t h o n 3 . o r g / examples / f e e d 301. xml ) >>> r e s p o n s e 2 . fromcache True >>> c o n t e n t 2 == c o n t e n t True

329

1. Lnea 3: Existe una diferencia entre las redirecciones temporales y permanen tes: una vez que httplib2 sigue una redireccin permanente, todas las peticiones o siguientes a la misma URl sern solitadas de forma transparente a la nueva URL a sin pasar por la URL original. Recuerda, la depuracin est an activada y sin o a u embargo en este caso no se observa actividad en la red de ningn tipo. u 2. Lnea 4: S la respuesta se obtuvo de la cach local. , e 3. Lnea 6: S el resultado es el ujo completo (de la cach). , e HTTP funciona.

14.6.

Un paso ms all de HTTP GET a a

Los servicios HTTP no se limitan a peticiones GET. Qu sucede si quieres crear e algo nuevo? Siempre que env un comentario a un foro, actualizas tu blog, publicas as tu estado en un servicio de microblog (como Twitter o Identi.ca, posiblemente ests a usando HTTP POST. Tanto Twitter como Identi.ca ofrecen una API simple basada en HTTP para publicar y actualizar tu estado en 140 caracteres o menos. Vamos a ver la documentacin de la API de Identi.ca para actualizar tu estado15 : o Mtodo Identi.ca REST API: estados/actualizaciones Actualiza el e estado del usuario autenticado. Requiere el parmetro status especicado a ms abajo. La peticin debe ser un POST. a o URL - http://identi.ca/api/statuses/update.format Formatos - xml, json, rss, atom Mtodos HTTP - POST e Necesita autenticacin - true o
15

http://laconi.ca/trac/wiki/TwitterCompatibleAPI

330

CAP ITULO 14. SERVICIOS WEB HTTP Parmetros: status. Requerido. El texto de la actualizacin de tu estado. a o Codicacin URL. o

Cmo funciona esto? Para publicar un nuevo mensaje en Identi.ca, necesitas o enviar una peticin HTTP POST a https://identi.ca/api/statuses/update.format (La o parte del format no es parte de la URL; lo sustituyes por el formato de datos que quieres que el servidor retorne en respuesta a tu peticin. Por lo que si quieres la respuesta o en XML deber enviar la peticin a https://identi.ca/api/statuses/update.xml). La as o peticin debe incluir un parmetro denominado status que contiene el texto de la o a actualizacin de tu estado que desees. Y la peticin necesita autenticacin. o o o Autenticacin? Para poder actualizar tu estado en Identi.ca, necesitas probar o que eres quien dices que eres. Identi.ca no es una wiki; solamente puedes actualizar tu propio estado. Identica.ca utiliza autenticacin bsica HTTP16 (RFC 262717 ) o a sobre SSL para proporcionar una autenticacin segura pero sencilla de usar. htto plib2 soporta tanto SSL como autenticacin bsica HTTP, por lo que esta parte es o a sencilla. Una peticin POST es diferente a una GET, porque incluye informacin que o o hay que enviar al servidor. En el caso de este mtodo de la API de Identi.ca, se e requiere la informacin del parmetro status y deber estar en codicacin URL. o a a o Este es un formato muy simple de serializacin que toma un conjunto de parejas o clave-valor (como un diccionario) y lo transforma en una cadena.
1 >>> from u r l l i b . p a r s e import u r l e n c o d e 2 >>> data = { s t a t u s : Test update from Python 3 } 3 >>> u r l e n c o d e ( data ) 4 s t a t u s=Test+update+from+Python+3

1. Lnea 1: Python dispone de una funcin de utiliza para codicar en este for o mato cualquier diccionario: urllib.parse.urlencode(). 2. Lnea 2: Este es el tipo de diccionario que la API de Identi.ca est esperando. a Contiene una clave, status, cuyo valor es el texto de una actualizacin de o estado. 3. Lnea 3: As queda el diccionario una vez est en el formato codicado URL. a Esta es la informacin que deber enviarse por la red al servidor de Identi.ca o a en la peticin HTTP POST. o
16 17

http://en.wikipedia.org/wiki/Basic access authentication http://www.ietf.org/rfc/rfc2617.txt

14.6. UN PASO MAS ALLA DE HTTP GET


1 2 3 4 5 6 7 8 9 10 11 12 >>> >>> >>> >>> >>> >>> >>> ... ... ...

331

from u r l l i b . p a r s e import u r l e n c o d e import h t t p l i b 2 httplib2 . debuglevel = 1 h = h t t p l i b 2 . Http ( . c a c h e ) data = { s t a t u s : Test update from Python 3 } h . a d d c r e d e n t i a l s ( diveintomark , MY SECRET PASSWORD , i d e n t i . ca ) resp , content = h . request ( h t t p s : / / i d e n t i . ca / a p i / s t a t u s e s / update . xml , POST , u r l e n c o d e ( data ) , h e a d e r s={ Content Type : a p p l i c a t i o n /xwww form u r l e n c o d e d } )

1. Lnea 7: As es como httplib2 controla la autenticacin. Almacena tu cdigo o o de usuario y clave de acceso con el mtodo add credentials(). Cuando httplib2 e intenta realizar la peticin, el servidor responder con un cdigo de estado 401 o a o Unauthorized, y una lista de mtodos de autenticacin disponibles (en la cae o becera WWW-Authenticate). httlib2 construir automticamente una cabecera a a de Authorization y solicitar la URL. a 2. Lnea 10: El segundo parmetro del mtodo request es el tipo de peticin a e o HTTP, en este caso POST. 3. Lnea 11: El tercer parmetro es la informacin que se env al servidor. Es a o a tamos enviando el diccionario codicado en URL con el mensaje de estado. 4. Lnea 12: Finalmente, necesitamos decirle al servidor que la informacin en o viada se encuentra en el formato URL-encoded. nio en el que El tercer parmetro del mtodo add credentials() es el dom a e las credenciales son vlidas. Deber especicar esto siempre! Si dejas el a as dominio sin especicar y luego reutilizas el objeto httplib2.Http en un sitio diferente que requiera autenticacin, httplib2 podr acabar enviando el o a usuario y clave de una sede web a otra diferente. Esto es lo que pasa por debajo:

332

CAP ITULO 14. SERVICIOS WEB HTTP

1 # sigue d e l ejemplo a nt er io r 2 send : b POST / a p i / s t a t u s e s / update . xml HTTP/ 1 . 1 3 Host : i d e n t i . ca 4 Accept Encoding : i d e n t i t y 5 Content Length : 32 6 c o n t e n t type : a p p l i c a t i o n /xwww form u r l e n c o d e d 7 u s e r a g e n t : Python h t t p l i b 2 /$Rev : 259 $ 8 9 s t a t u s=Test+update+from+Python+3 10 r e p l y : HTTP/ 1 . 1 401 Unauthorized 11 send : b POST / a p i / s t a t u s e s / update . xml HTTP/ 1 . 1 12 Host : i d e n t i . ca 13 Accept Encoding : i d e n t i t y 14 Content Length : 32 15 c o n t e n t type : a p p l i c a t i o n /xwww form u r l e n c o d e d 16 a u t h o r i z a t i o n : B a s i c SECRET HASH CONSTRUCTED BY HTTPLIB2 17 u s e r a g e n t : Python h t t p l i b 2 /$Rev : 259 $ 18 19 s t a t u s=Test+update+from+Python+3 20 r e p l y : HTTP/ 1 . 1 200 OK

1. Lnea 10: Despus de la primera peticin, el servidor responde con un cdigo de e o o estado 401 Unauthorized. httplib2 nunca enviar una cabecera de autenticacin a o a no ser que el servidor la solicite expresamente. Esta es la forma en la que el servidor lo hace. 2. Lnea 11: httplib2 inmediatamente vuelve a pedir la URL por segunda vez. 3. Lnea 16: Esta vez, incluye el cdigo de usuario y clave de acceso que aadiste o n en el mtodo add credentials(). e 4. Lnea 20: Funcion! o Qu env el servidor despus de una peticin que se complet satisfactoe a e o o riamente? Depende totalmente de la API del servicio web. En algunos protocolos (como el protocolo de publicacin Atom) el servidor devuelve un cdigo de estado o o 201 Created y la localizacin del recurso recin creado en la cabecera Location. Ideno e ti.ca responde con un 200 OK y un documento XML que contiene informacin sobre o el recurso recin creado. e

14.6. UN PASO MAS ALLA DE HTTP GET


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 # sigue d e l ejemplo a nt er io r >>> print ( c o n t e n t . decode ( u t f 8 ) ) <?xml v e r s i o n= 1 . 0 e n c o d i n g=UTF8?> <s t a t u s > <t e x t >Test update from Python 3</ t e x t > <t r u n c a t e d >f a l s e </t r u n c a t e d > <c r e a t e d a t >Wed Jun 10 0 3 : 5 3 : 4 6 +0000 2009</ c r e a t e d a t > < i n r e p l y t o s t a t u s i d ></ i n r e p l y t o s t a t u s i d > <s o u r c e >api </s o u r c e > <id >5131472</ id> < i n r e p l y t o u s e r i d ></ i n r e p l y t o u s e r i d > <i n r e p l y t o s c r e e n n a m e ></i n r e p l y t o s c r e e n n a m e > <f a v o r i t e d >f a l s e </ f a v o r i t e d > <u s e r > <id >3212</ id> <name>Mark P i l g r i m </name> <screen name >d i v e i n t o m a r k </screen name > <l o c a t i o n >27502 , US</ l o c a t i o n > <d e s c r i p t i o n >t e c h w r i t e r , husband , f a t h e r </ d e s c r i p t i o n > <p r o f i l e i m a g e u r l >h t t p : / / a v a t a r . i d e n t i . ca / 3212 48 20081216000626. png</ p r o f i l e i m a g e u r l > <u r l >h t t p : / / d i v e i n t o m a r k . o r g /</ u r l > <p r o t e c t e d >f a l s e </p r o t e c t e d > <f o l l o w e r s c o u n t >329</ f o l l o w e r s c o u n t > <p r o f i l e b a c k g r o u n d c o l o r ></p r o f i l e b a c k g r o u n d c o l o r > < p r o f i l e t e x t c o l o r ></ p r o f i l e t e x t c o l o r > < p r o f i l e l i n k c o l o r ></ p r o f i l e l i n k c o l o r > < p r o f i l e s i d e b a r f i l l c o l o r ></ p r o f i l e s i d e b a r f i l l c o l o r > < p r o f i l e s i d e b a r b o r d e r c o l o r ></ p r o f i l e s i d e b a r b o r d e r c o l o r > <f r i e n d s c o u n t >2</ f r i e n d s c o u n t > <c r e a t e d a t >Wed J u l 02 2 2 : 0 3 : 5 8 +0000 2008</ c r e a t e d a t > <f a v o u r i t e s c o u n t >30768</ f a v o u r i t e s c o u n t > < u t c o f f s e t >0</ u t c o f f s e t > <t i m e z o n e >UTC</t i m e z o n e > <p r o f i l e b a c k g r o u n d i m a g e u r l ></p r o f i l e b a c k g r o u n d i m a g e u r l > <p r o f i l e b a c k g r o u n d t i l e >f a l s e </ p r o f i l e b a c k g r o u n d t i l e > <s t a t u s e s c o u n t >122</ s t a t u s e s c o u n t > <f o l l o w i n g >f a l s e </ f o l l o w i n g > < n o t i f i c a t i o n s >f a l s e </ n o t i f i c a t i o n s > </u s e r > </ s t a t u s >

333

1. Lnea 2: Recuerda, los datos devueltos por httplib2 son siempre bytes, no cade nas de texto. Para convertirlos a una cadena de texto, necesitas decodicarlos utilizando la codicacin de caracteres apropiada. La API de Identi.ca siempre o devuelve los resultados en UTF-8, as que esto es fcil. a 2. Lnea 5: Este es el texto del mensaje de estado recin publicado. e

334

CAP ITULO 14. SERVICIOS WEB HTTP

3. Lnea 10: Este es el identicador unico del nuevo mensaje. Idnti.ca lo utiliza pa ra construir una URL para poder ver el mensaje en la web: http://identi.ca/notice/5131472

14.7.

Ms all de HTTP POST a a

HTTP no est limitado a GET y POST. Son los dos tipos ms comunes de a a peticiones, especialmente en los navegadores web. Pero las APIs de servicios web pueden ir ms all de ellos, y httplib2 est preparado. a a a
1 2 3 4 5 6 7 8 9 # sigue d e l ejemplo a nt er io r >>> from xml . e t r e e import ElementTree a s e t r e e >>> t r e e = e t r e e . f r o m s t r i n g ( c o n t e n t ) >>> s t a t u s i d = t r e e . f i n d t e x t ( i d ) >>> s t a t u s i d 5131472 >>> u r l = h t t p s : / / i d e n t i . ca / a p i / s t a t u s e s / d e s t r o y / { 0 } . xml . format ( status id ) >>> r e s p , d e l e t e d c o n t e n t = h . r e q u e s t ( u r l , DELETE )

1. Lnea 3: El servidor retorn XML correcto? Ya sabes cmo analizar un docu o o mento XML. 2. Lnea 4: El mtodo ndtext() encuentra la primera ocurrencia de una expresin e o dada y devuelve el texto del contenido. En este caso, unicamente estamos buscando el elemento id. 3. Lnea 8: Basndonos en el contenido del elemento id podemos construir una a URL para borrar el mensaje de estado que acabamos de publicar. 4. L nea 9: Para borrar un mensaje, simplemente env una peticin HTTP DEa o LETE a esa URL. Esto es lo que sucede por debajo:

14.8. LECTURAS RECOMENDADAS


1 send : b DELETE / a p i / s t a t u s e s / d e s t r o y / 5 1 3 1 4 7 2 . xml HTTP/ 1 . 1 2 Host : i d e n t i . ca 3 Accept Encoding : i d e n t i t y 4 u s e r a g e n t : Python h t t p l i b 2 /Rev : 259 5 6 7 r e p l y : HTTP/ 1 . 1 401 Unauthorized 8 send : b DELETE / a p i / s t a t u s e s / d e s t r o y / 5 1 3 1 4 7 2 . xml HTTP/ 1 . 1 9 Host : i d e n t i . ca 10 Accept Encoding : i d e n t i t y 11 a u t h o r i z a t i o n : B a s i c SECRET HASH CONSTRUCTED BY HTTPLIB2 12 u s e r a g e n t : Python h t t p l i b 2 /Rev : 259 13 14 15 r e p l y : HTTP/ 1 . 1 200 OK 16 >>> r e s p . s t a t u s 17 200

335

1. Lnea 1: Borra este mensaje de estado. 2. Lnea 7: Lo siento, no puedo hacer eso sin saber quin lo pide e 3. Lnea 8: No tengo permiso? Soy yo, borra el mensaje, por favor... 4. Lnea 11: ...y estos son mi cdigo de usuario y clave de acceso. o 5. Lnea 15: Considralo hecho. e As que ahora al intentar el enlace http://identi.ca/notice/5131472 en un na vegador, se obtiene Not Found.

14.8.
httplib2:

Lecturas recomendadas

Pgina del proyecto httplib2: a http://code.google.com/p/httplib2/ Ms ejemplos de cdigo httplib2: a o http://code.google.com/p/httplib2/wiki/ExamplesPython3 Cmo hacer correctamente la cach de HTTP: Introduccin a httplib2: o e o http://www.xml.com/pub/a/2006/02/01/doing-http-caching-right-introducinghttplib2.html

336

CAP ITULO 14. SERVICIOS WEB HTTP httplib2: Persistencia y autenticacin HTTP: o http://www.xml.com/pub/a/2006/03/29/httplib2-http-persistence-and-authentication.html

Cach HTTP: e Tutorial sobre la cach en HTTP de Mark Nottingham: e http://www.mnot.net/cache docs/ Cmo controlar la cach con cabeceras de HTTP en Google Doctype o e http://code.google.com/p/doctype/wiki/ArticleHttpCaching RFCs: RFC 2616: HTTP http://www.ietf.org/rfc/rfc2616.txt RFC 2617: HTTP, autenticacin bsica o a http://www.ietf.org/rfc/rfc2617.txt RFC 1951: formato de compresin deate o http://www.ietf.org/rfc/rfc1951.txt RFC 1952: formato de compresin gzip o http://www.ietf.org/rfc/rfc1952.txt

Potrebbero piacerti anche