Sunteți pe pagina 1din 342

.

www.detodoprogramacion.com

Libros Universitarios, juegos, Revistas, Cursos, Software, Sistemas


Operativos, Antivirus y ms Gratis para el Conocimiento...!
www.detodoprogramas.com
Vistanos y comprubalo

Material para los amantes de la Programacin Java,


C/C++/C#,Visual.Net, SQL, Python, Javascript, Oracle, Algoritmos,
CSS, Desarrollo Web, Joomla, jquery, Ajax y Mucho Mas
www.detodoprogramacion.com
Visitanos

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.

www.detodoprogramacion.com

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.

www.detodoprogramacion.com

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

www.detodoprogramacion.com


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

www.detodoprogramacion.com

-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

www.detodoprogramacion.com

CAP
ITULO -1. NOVEDADES DE INMERSION EN PYTHON 3

www.detodoprogramacion.com

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
1

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

www.detodoprogramacion.com


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

www.detodoprogramacion.com


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

www.detodoprogramacion.com


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
5

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.

www.detodoprogramacion.com


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

www.detodoprogramacion.com


CAP
ITULO 0. INSTALACION DE PYTHON

10

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

www.detodoprogramacion.com


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

www.detodoprogramacion.com


CAP
ITULO 0. INSTALACION DE PYTHON

12

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

www.detodoprogramacion.com


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

www.detodoprogramacion.com

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.

www.detodoprogramacion.com


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

www.detodoprogramacion.com

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

www.detodoprogramacion.com


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

www.detodoprogramacion.com

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

www.detodoprogramacion.com


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

www.detodoprogramacion.com

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

www.detodoprogramacion.com


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

www.detodoprogramacion.com

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.

www.detodoprogramacion.com


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

www.detodoprogramacion.com

24

CAP
ITULO 0. INSTALACION DE PYTHON

Figura 28: Descarga de paquetes

Figura 29: Descarga de paquetes

Figura 30: Instalacin nalizada


o

www.detodoprogramacion.com


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

www.detodoprogramacion.com


CAP
ITULO 0. INSTALACION DE PYTHON

26

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.
9

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

www.detodoprogramacion.com

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

www.detodoprogramacion.com


CAP
ITULO 0. INSTALACION DE PYTHON

28
help> quit

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.
10

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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

30

CAP
ITULO 0. INSTALACION DE PYTHON

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 1. TU PRIMER PROGRAMA EN PYTHON

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

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,
1

Para que funcione correctamente debes moverte al directorio en el que est grabado el chero
e
parahumanos.py.

www.detodoprogramacion.com


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
2

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

www.detodoprogramacion.com

CAP
ITULO 1. TU PRIMER PROGRAMA EN PYTHON

34

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

www.detodoprogramacion.com


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

www.detodoprogramacion.com

CAP
ITULO 1. TU PRIMER PROGRAMA EN PYTHON

36

4. L
nea 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. L
nea 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

www.detodoprogramacion.com


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).
5

En espaol se usa tambin ruta de bsqueda. En ingls se usa la palabra path para referirse a
n
e
u
e
este concepto

www.detodoprogramacion.com

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. L
nea 1: Al importar el paquete sys de esta forma, todas sus funciones y
atributos quedan a disposicin del programador para su uso.
o
2. L
neas 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. L
neas 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. L
nea 11: En tiempo de ejecucin puedes aadir un nuevo directorio al cao
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

www.detodoprogramacion.com

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.

www.detodoprogramacion.com

CAP
ITULO 1. TU PRIMER PROGRAMA EN PYTHON

40

2. L
nea 2: Cuando quieres utilizar las funciones que estn denidas en los mdue
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. L
nea 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 podr
a
e
an. No
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

www.detodoprogramacion.com


1.6. INDENTAR CODIGO

1.6.

41

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
for 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.

www.detodoprogramacion.com

CAP
ITULO 1. TU PRIMER PROGRAMA EN PYTHON

42

5. L
nea 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 .

1.7.

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.

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

www.detodoprogramacion.com

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!
8

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

www.detodoprogramacion.com

CAP
ITULO 1. TU PRIMER PROGRAMA EN PYTHON

44

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
9

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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 1. TU PRIMER PROGRAMA EN PYTHON

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

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

www.detodoprogramacion.com

1.11. LECTURAS COMPLEMENTARIAS

47

1 >>> import parahumanos


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

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.

www.detodoprogramacion.com

48

CAP
ITULO 1. TU PRIMER PROGRAMA EN PYTHON

www.detodoprogramacion.com

Cap
tulo 2
Tipos de dato nativos
Nivel de dicultad:   
La curiosidad es la base de toda la losofa,

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.
1

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.

49

www.detodoprogramacion.com

CAP
ITULO 2. TIPOS DE DATO NATIVOS

50

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

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
2

Nota del traductor: Son tipos de dato del lenguaje Python que representan a: mdulos, funo
ciones, clases, mtodos, cheros y cdigo compilado.
e
o

www.detodoprogramacion.com


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

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

www.detodoprogramacion.com

CAP
ITULO 2. TIPOS DE DATO NATIVOS

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 >

1. L
nea 1: la funcin type() permite consultar el tipo de cualquier valor o variao
ble. Como era de esperar 1 es un valor de tipo int.
2. L
nea 3: la funcin isinstance() permite chequear si un valor o variable es de
o
un tipo determinado.
3. L
nea 5: La suma de dos valores de tipo int da como resultado otro valor de
tipo int.
4. L
nea 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. L
nea 1: Utilizando la funcin oat() puedes convertir expl
o
citamente un valor
de tipo int en oat.

www.detodoprogramacion.com


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.
5

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

www.detodoprogramacion.com

CAP
ITULO 2. TIPOS DE DATO NATIVOS

54

2. L
nea 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. L
nea 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. L
nea 7: El operador ** signica elevado a la potencia de. 112 es 121.
5. L
nea 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. L
nea 1: Para comenzar a utilizar fracciones hay que importar el mdulo fraco
tions.

www.detodoprogramacion.com


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.

www.detodoprogramacion.com

CAP
ITULO 2. TIPOS DE DATO NATIVOS

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 )
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. L
nea 1: Sab que puedes denir tus propias funciones en la consola interas
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. L
nea 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. L
nea 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

www.detodoprogramacion.com

2.4. LISTAS

2.4.

57

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

www.detodoprogramacion.com

CAP
ITULO 2. TIPOS DE DATO NATIVOS

58

2. L
nea 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. L
nea 6: El ultimo elemento de esta lista de cinco elementos es lista[4], puesto

que los elementos se indexan contando desde cero.


4. L
nea 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. L
nea 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. L
nea 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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 2. TIPOS DE DATO NATIVOS

60

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. L
nea 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. L
nea 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. L
nea 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 ] ]

www.detodoprogramacion.com

2.4. LISTAS

61

1. Lnea 2: El mtodo extend() recibe un unico parmetro, que siempre es una

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

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

www.detodoprogramacion.com

CAP
ITULO 2. TIPOS DE DATO NATIVOS

62

3. L
nea 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. L
nea 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. L
nea 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. L
nea 4: Para eliminar un elemento de una lista puedes utilizar la sentencia
del.

www.detodoprogramacion.com

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.

www.detodoprogramacion.com

CAP
ITULO 2. TIPOS DE DATO NATIVOS

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

>>> 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. L
nea 2: Cuando se llama sin parmetros, el mtodo pop() elimina el ultimo
a
e

valor de la lista y devuelve el valor eliminado.


2. L
nea 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. L
nea 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.

www.detodoprogramacion.com

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

>>>
...
...
...
...
...
>>>
no ,
>>>
s ,

>>>
s ,

65

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

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.

www.detodoprogramacion.com

CAP
ITULO 2. TIPOS DE DATO NATIVOS

66

4. L
nea 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. L
nea 4: No puedes aadir elementos a una tupla. No existen los mtodos
n
e
append() o extend().
2. L
nea 8: No puedes eliminar elementos de una tupla. No existen los mtodos
e
remove() o pop().
3. L
nea 12: S puedes buscar elementos en una tupla puesto que consultar no

cambia la tupla.
4. L
nea 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.

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 2. TIPOS DE DATO NATIVOS

68

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. L
nea 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. L
nea 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. L
nea 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.
8

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

www.detodoprogramacion.com

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 ]

www.detodoprogramacion.com

CAP
ITULO 2. TIPOS DE DATO NATIVOS

70

1. L
nea 2: Para crear un conjunto de una lista utiliza la funcin set()10 .
o
2. L
nea 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. L
nea 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. L
nea 1: Para crear un conjunto vac debes utilizar la funcin set() sin parmeo
o
a
tros.
2. L
nea 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. L
nea 4: A pesar de la extraa representacin impresa se trata de un conjunto.
n
o
4. L
nea 6: ...y este conjunto no tiene elementos.
5. L
nea 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.
10

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

www.detodoprogramacion.com

2.6. CONJUNTOS

2.6.2.

71

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

www.detodoprogramacion.com

CAP
ITULO 2. TIPOS DE DATO NATIVOS

72

2. L
nea 5: Los elementos duplicados se ignoran puesto que los conjuntos no
pueden contener duplicados.
3. L
nea 7: Puedes llamar al mtodo update() con cualquier nmero de parmee
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. L
nea 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. L
nea 4: El mtodo discard() toma un unico parmetro y elminia el elemento
e

a
del conjunto.
2. L
nea 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. L
nea 10: El mtodo remove() tambin recibe un unico parmetro y tambin
e
e

a
e
elimina el elemento del conjunto.
4. L
nea 13: Aqu esta la diferencia: si el valor no existe en el conjunto, el mtodo

e
remove() eleva la excepcin KeyError.
o

www.detodoprogramacion.com

2.6. CONJUNTOS

73

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

1. Lnea 2: El mtodo pop() elimina un unico valor del conjunto y retorna el

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:

www.detodoprogramacion.com

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. L
nea 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. L
nea 7: El mtodo union() retorna un nuevo conjunto que contiene todos los
e
elementos que estn en alguno de los conjuntos originales.
a
3. L
nea 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. L
nea 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

www.detodoprogramacion.com

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.

www.detodoprogramacion.com

CAP
ITULO 2. TIPOS DE DATO NATIVOS

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

1. L
nea 7: En un contexto booleano los conjuntos vac valen False.
os
2. L
nea 9: Cualquier conjunto con al menos un elemento vale True.
3. L
nea 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.
11

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

www.detodoprogramacion.com

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 }

www.detodoprogramacion.com

CAP
ITULO 2. TIPOS DE DATO NATIVOS

78

1. L
nea 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. L
nea 8: El elemento nuevo del diccionario (clave usuario, valor mark) aparece en la mitad. Esto es una mera coincidencia, los elementos de un diccionario
no estn ordenados.
a
4. L
nea 10: Al asignar un valor a una clave existente, simplemente se sustituye
el valor anterior por el nuevo.
5. L
nea 14: Esta sentencia cambia el valor de la clave usuario para volverle 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.

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 2. TIPOS DE DATO NATIVOS

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

1. L
nea 7: En un contexto booleano un diccionario vac equivale a False.
o
2. L
nea 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

www.detodoprogramacion.com

2.9. LECTURAS COMPLEMENTARIAS

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

www.detodoprogramacion.com

81

82

CAP
ITULO 2. TIPOS DE DATO NATIVOS

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 3. COMPRENSIONES

84

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. L
nea 1: El mdulo os viene instalado con Python. Puedes importarlo siempre
o
que lo necesites.
2. L
nea 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

www.detodoprogramacion.com

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, ...

www.detodoprogramacion.com

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. L
nea 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. L
nea 2: La funcin split() divide un camino completo en dos partes que cono
tienen el camino y el nombre de chero. Los retorna en una tupla.
2. L
nea 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. L
nea 5: La primera variable, nombredir, recibe el valor del primer elemento
de la tupla que retorna os.path.split(), el camino al chero.
4. L
nea 7: La segunda variable, nombrech, recibe el valor del segundo elemento
de la tupla que retorna os.path.split(), el nombre del chero.

www.detodoprogramacion.com

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 comod que suelen usarse en una consola de l
n
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 ma ntes t1 . py ,
r o man test 10 . py ,
r o ma ntes t2 . py ,
r o ma ntes t3 . py ,
r o ma ntes t4 . py ,
r o ma ntes t5 . py ,
r o ma ntes t6 . py ,
r o ma ntes t7 . py ,
r o ma ntes t8 . py ,
r o ma ntes t9 . 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

www.detodoprogramacion.com

CAP
ITULO 3. COMPRENSIONES

88

funcin os.chdir() puede recibir como parmetro un camino relativo a la posio


a
cin actual.
o
3. L
nea 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. L
nea 2: El directorio de trabajo actual es ejemplos.
2. L
nea 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. L
nea 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. L
nea 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

www.detodoprogramacion.com


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.

www.detodoprogramacion.com

CAP
ITULO 3. COMPRENSIONES

90
1
2
3
4
5
6
7
8

>>>
>>>
[2 ,
>>>
[1 ,
>>>
>>>
[2 ,

una lista = [1 , 9 , 8 , 4]
[ elem 2 for 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 for elem in u n a l i s t a ]
una lista
18 , 16 , 8 ]

1. L
nea 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. L
nea 4: Como se observa, la lista original no cambia.
3. L
nea 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 ) for 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. L
nea 2: Esta llamada retorna una lista con todos los cheros terminados en
.xml del directorio de trabajo.
2. L
nea 4: Esta lista generada por comprensin toma la lista original y la transo
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

www.detodoprogramacion.com


3.3. LISTAS POR COMPRENSION

91

1 >>> import os , g l o b
2 >>> [ f for 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 man test 10 . py ,
5
r o ma ntes t6 . py ,
6
r o ma ntes t7 . py ,
7
r o ma ntes t8 . py ,
8
r o ma ntes t9 . py ]

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

www.detodoprogramacion.com

CAP
ITULO 3. COMPRENSIONES

92

3.4.

Diccionarios por comprensin


o

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


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

>>> 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 ntes t8 . 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 , rom ante st 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 ntes t9 . 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 ntes t3 . py , rom ante st5 . 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. L
nea 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. L
nea 3: Cada elemento de la lista resultante es una tupla.
3. L
nea 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. L
nea 8: El resultado es un diccionario.
5. L
nea 10: La claves de este caso particular son los nombres de los cheros.
6. L
nea 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.

www.detodoprogramacion.com


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 ) \
...
for 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 ( ) )
[ r o m ante st9 , ro man test 8 , r o m a n t e s t 7 , r o m a n t e s t 6 ,
r o man test 10 , p l u r a l t e s t 6 ]
>>> d i c t [ r oman test 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 for 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.

www.detodoprogramacion.com

CAP
ITULO 3. COMPRENSIONES

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 for 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 for x in c o n j u n t o i f x % 2 == 0}
{ 0 , 8 , 2 , 4 , 6}
>>> {2 x for 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}

1. L
nea 4: Los conjuntos generados por comprensin pueden partir de otro cono
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. L
nea 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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

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

www.detodoprogramacion.com


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

www.detodoprogramacion.com

CAP
ITULO 4. CADENAS DE TEXTO

98

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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 4. CADENAS DE TEXTO

100

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. L
nea 1: Para crear una cadena de texto debes utilizar las comillas para delimitarla. Se pueden utilizar comillas simples () o dobles ().
4

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

www.detodoprogramacion.com

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:

www.detodoprogramacion.com

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. L
nea 3: KB, MB, GB, ... son cadenas.
2. L
nea 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. L
nea 18: Estas comillas triples nalizan la cadena de documentacin de esta
o
funcin.
o
4. L
nea 20: Otra cadena que se pasa como parmetro a la excepcin con el n
a
o
de que sea legible por una persona.
5. L
nea 26: Esto es... U! Qu car.. es esto?
e

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 4. CADENAS DE TEXTO

104

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

www.detodoprogramacion.com

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.

www.detodoprogramacion.com

CAP
ITULO 4. CADENAS DE TEXTO

106

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. L
nea 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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 4. CADENAS DE TEXTO

108

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. L
nea 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. L
nea 4: Como sucede con las listas, puedes utilizar
ndices negativos para
seleccionar.
3. L
nea 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. L
nea 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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 4. CADENAS DE TEXTO

110

4. L
nea 8: Como sucede con las listas y cadenas, puedes utilizar el operador +
para concatenar objetos bytes. El resultado es un nuevo objeto bytes.
5. L
nea 11: Concatenar un objeto bytes de 5 bytes con uno de 1 byte da como
resultado un objeto bytes de 6 bytes.
6. L
nea 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. L
nea 15: Un objeto bytes es inmutable; no puedes asignar bytes individuales. 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. L
nea 2: Para convertir un objeto bytes en un objeto modicable de tipo bytearray puedes utilizar la funcin interna bytearray().
o
2. L
nea 5: Todos los mtodos y operaciones que existen en el objeto bytes tame
bin estn disponibles en el objeto bytearray.
e
a
3. L
nea 7: Una de las diferencias es que al objeto bytearray es posible modicarle 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.

www.detodoprogramacion.com

4.6. CADENAS DE TEXTO Y BYTES


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

111

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

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

www.detodoprogramacion.com

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. L
nea 1: Esto es una cadena de texto, tiene 9 caracteres.
2. L
nea 4: El resultado de codicar la cadena en UTF-8 es un objeto bytes. Tiene
13 bytes.
3. L
nea 9: El resultado de codicar la cadena en GB18030 es un objeto bytes de
11 bytes.
4. L
nea 14: El resultado de codicar la cadena en UTF-16 es un objeto bytes de
20 bytes.
5. L
nea 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.

www.detodoprogramacion.com

4.8. LECTURAS RECOMENDADAS

113

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

www.detodoprogramacion.com

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/

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 5. EXPRESIONES REGULARES

116

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. L
nea 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. L
nea 5: La vida, desafortunadamente, est llena de contraejemplos, y rpidaa
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.

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 5. EXPRESIONES REGULARES

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

>>> 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. L
nea 4: Lo que yo realmente quer era buscar ROAD cuando estuviera al
a
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. L
nea 6: Para evitar la plaga de las barras inclinadas invertidas puedes utilizar 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. L
nea 9: Desafortunadamente rpidamente encontr ms casos que contradia
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

www.detodoprogramacion.com


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

www.detodoprogramacion.com

CAP
ITULO 5. EXPRESIONES REGULARES

120

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. L
nea 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

www.detodoprogramacion.com


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

www.detodoprogramacion.com

CAP
ITULO 5. EXPRESIONES REGULARES

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

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>

www.detodoprogramacion.com


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

www.detodoprogramacion.com

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. L
nea 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. L
nea 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. L
nea 9: El patrn coincide con el inicio de la cadena, luego las tres M para
o
terminar con el nal de la cadena.
4. L
nea 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. L
nea 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

www.detodoprogramacion.com


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

www.detodoprogramacion.com

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. L
nea 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. L
nea 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. L
nea 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. L
nea 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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 5. EXPRESIONES REGULARES

128

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. L
nea 12: Lo ms importante es recordar que cuando utilizas expresiones rea
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. L
nea 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.

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 5. EXPRESIONES REGULARES

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

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. L
nea 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. L
nea 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. L
nea 4: Esta expresin regular no es perfecta, porque no es capaz de mao
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
,

www.detodoprogramacion.com

5.6. CASO DE ESTUDIO: ANALISIS DE NUMEROS DE TELEFONO


1
2
3
4
5
6
7

131

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

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

www.detodoprogramacion.com

132

CAP
ITULO 5. EXPRESIONES REGULARES

1. L
nea 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. L
nea 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. L
nea 4: Por supuesto an se encuentran los nmeros de telfonos que utilizan
u
u
e
como separador el guin.
o
4. L
nea 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. L
nea 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. L
nea 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.

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 5. EXPRESIONES REGULARES

134

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. L
nea 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. L
nea 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. L
nea 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. L
nea 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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

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.

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 6. CIERRES Y GENERADORES

138

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!

www.detodoprogramacion.com


6.2. LO SE, VAMOS A USAR EXPRESIONES REGULARES!
1
2
3
4
5
6
7
8
9
10
11

139

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

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.

www.detodoprogramacion.com

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. L
nea 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. L
nea 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. L
nea 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. L
nea 2: La palabra vacancy coincide con esta expresin regular, porque naliza
o
en cy, y la c no es una vocal.

www.detodoprogramacion.com


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

www.detodoprogramacion.com

CAP
ITULO 6. CIERRES Y GENERADORES

142

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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 6. CIERRES Y GENERADORES

144

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. L
nea 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

www.detodoprogramacion.com

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 ( p a t t e r n , s e a r c h ,

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

www.detodoprogramacion.com

CAP
ITULO 6. CIERRES Y GENERADORES

146

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. L
nea 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.

www.detodoprogramacion.com

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

147

es
es
ies
s

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

www.detodoprogramacion.com

CAP
ITULO 6. CIERRES Y GENERADORES

148

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. L
nea 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 :
for 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

www.detodoprogramacion.com

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

149

>>> def make counter ( x ) :


...
print ( e n t ra 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 nd 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

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
2

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

www.detodoprogramacion.com

CAP
ITULO 6. CIERRES Y GENERADORES

150

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. L
nea 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. L
nea 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. L
nea 4: a es el nmero actual de la secuencia, por lo que se puede ceder el
u
control y retornar dicho valor.
3. L
nea 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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

152

CAP
ITULO 6. CIERRES Y GENERADORES

1. L
nea 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. L
nea 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. L
nea 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.

www.detodoprogramacion.com

6.9. LECTURAS RECOMENDADAS

6.9.

153

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

www.detodoprogramacion.com

154

CAP
ITULO 6. CIERRES Y GENERADORES

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 7. CLASES E ITERADORES

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

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

def

iter ( self ):
self .a = 0
self .b = 1
return s e l f

def

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

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. L
nea 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. L
nea 2: Probablemente lo hasta adivinado, pero el contenido de la clase

www.detodoprogramacion.com


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

www.detodoprogramacion.com

CAP
ITULO 7. CLASES E ITERADORES

158

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. L
nea 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. L
nea 3: La variable b se reere ahora a un objeto que es instancia de la clase
Fib.
3. L
nea 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. L
nea 7: Puedes acceder al docstring de la instancia como se hace con cualquier

www.detodoprogramacion.com

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

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

www.detodoprogramacion.com

CAP
ITULO 7. CLASES E ITERADORES

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

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

def

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

1. L
nea 1: Para poder construir un iterador desde cero b necesita ser una clase,
no una funcin.
o
2. L
nea 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

www.detodoprogramacion.com

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 >>> for 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.

www.detodoprogramacion.com

CAP
ITULO 7. CLASES E ITERADORES

162

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

www.detodoprogramacion.com


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

163

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 = [ ]

def

iter ( self ):
s e l f . cache index = 0
return s e l f

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

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

www.detodoprogramacion.com

164

CAP
ITULO 7. CLASES E ITERADORES

2. L
nea 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. L
nea 4: Cada instancia de la clase hereda el atributo rules lename con el
valor denido para la clase.
2. L
nea 8: La modicacin del valor de la variable en una instancia no afecta al
o
valor de las otras instancias...
3. L
nea 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. L
nea 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. L
nea 18: Todas las instancias que han modicado ese atributo, sustituyendo
su valor (como r2 aqu no se vern afectadas.
)
a

www.detodoprogramacion.com


7.6. UN ITERADOR PARA REGLAS DE FORMACION DE PLURALES

165

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

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

www.detodoprogramacion.com

CAP
ITULO 7. CLASES E ITERADORES

166

1. L
nea 5: Veamos una tcnica avanzada de acceso a cheros. El mtodo reade
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. L
nea 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. L
nea 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 ()...

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

1. L
nea 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. L
nea 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

www.detodoprogramacion.com


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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

7.7. LECTURAS RECOMENDADAS

7.7.

169

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/

www.detodoprogramacion.com

170

CAP
ITULO 7. CLASES E ITERADORES

www.detodoprogramacion.com

Libros Universitarios, juegos, Revistas, Cursos, Software, Sistemas


Operativos, Antivirus y ms Gratis para el Conocimiento...!
www.detodoprogramas.com
Vistanos y comprubalo

Material para los amantes de la Programacin Java,


C/C++/C#,Visual.Net, SQL, Python, Javascript, Oracle, Algoritmos,
CSS, Desarrollo Web, Joomla, jquery, Ajax y Mucho Mas
www.detodoprogramacion.com
Visitanos

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

www.detodoprogramacion.com

CAP
ITULO 8. ITERADORES AVANZADOS

172

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

www.detodoprogramacion.com


8.2. ENCONTRAR TODAS LAS OCURRENCIAS DE UN PATRON

173

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

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 ]

www.detodoprogramacion.com

CAP
ITULO 8. ITERADORES AVANZADOS

174

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 }

www.detodoprogramacion.com

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 for v e r y l a r g e v a l u e s o f 2

www.detodoprogramacion.com

CAP
ITULO 8. ITERADORES AVANZADOS

176

1. L
nea 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. L
nea 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 ) for 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 ) for 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. L
nea 2: Una expresin generadora es como una funcin annima que va deo
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

www.detodoprogramacion.com


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

177

2. Lnea 3: La expresin generadora devuelve... un iterador.

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


for 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

www.detodoprogramacion.com

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. L
nea 1: El mdulo itertools contiene toda clase de funciones utiles, incluida
o

una denominada permutations() que hace el trabajo duro de encontrar permutaciones.


2. L
nea 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. L
nea 3: La primera permutacin de [1, 2, 3] tomando dos elementos cada vez
o
es (1, 2).
4. L
nea 8: Observa que las permutaciones que se van obteniendo son ordenadas:
(2, 1) es diferente a (1, 2).
5. L
nea 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

www.detodoprogramacion.com


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

www.detodoprogramacion.com

180

CAP
ITULO 8. ITERADORES AVANZADOS

1. L
nea 2: La funcin itertools.product() devuelve un iterador que contiene el
o
producto cartesiano de dos secuencias.
2. L
nea 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 ( ) fo 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. L
nea 1: Esta forma de leer un chero retorna una lista formada por todas las
l
neas del chero de texto.
2. L
nea 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. L
nea 9: La funcin sorted() toma una lista y la retorna ordenada. Por defecto,
o
la ordena alfabticamente.
e
4. L
nea 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

www.detodoprogramacion.com


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
>>> gr ou ps = i t e r t o o l s . groupby ( names , l e n )
>>> gr ou ps
< i t e r t o o l s . groupby o b j e c t a t 0x00BB20C0>
>>> l i s t ( g ro ups )
[ ( 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 >)]
>>> gr ou ps = i t e r t o o l s . groupby ( names , l e n )
>>> for 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 ) )
...
for 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

www.detodoprogramacion.com

CAP
ITULO 8. ITERADORES AVANZADOS

182

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. L
nea 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. L
nea 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. L
nea 9: La funcin zip() para cuando se acaba la secuencia ms corta. rano
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. L
nea 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.

www.detodoprogramacion.com


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

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 ) for 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.

www.detodoprogramacion.com

184

8.7.

CAP
ITULO 8. ITERADORES AVANZADOS

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. L
nea 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. L
nea 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. L
nea 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 ) for 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. L
nea 1: Mediante una expresin generadora calculamos rpidamente los vao
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

www.detodoprogramacion.com


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

www.detodoprogramacion.com

CAP
ITULO 8. ITERADORES AVANZADOS

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 )
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. L
nea 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. L
nea 4: Y funciones.
3. L
nea 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. L
nea 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. L
nea 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

www.detodoprogramacion.com


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

www.detodoprogramacion.com

CAP
ITULO 8. ITERADORES AVANZADOS

188

2. L
nea 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. L
nea 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. L
nea 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. L
nea 3: Desafortunadamente (y si no ves porqu es desafortunado, sigue lee
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

www.detodoprogramacion.com

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.

www.detodoprogramacion.com

CAP
ITULO 8. ITERADORES AVANZADOS

190

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.

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 9. PRUEBAS UNITARIAS

192

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

www.detodoprogramacion.com


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:

Toda prueba es una isla.

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

www.detodoprogramacion.com

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
for 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. L
nea 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.

www.detodoprogramacion.com


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.

www.detodoprogramacion.com

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. L
nea 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 roma ntes t1 . 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. L
nea 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. L
nea 3: Para cada caso de prueba, el mdulo unittest imprimir el docstring
o
a

www.detodoprogramacion.com


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 =
for 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

www.detodoprogramacion.com

198

CAP
ITULO 9. PRUEBAS UNITARIAS

1. L
nea 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. L
nea 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

www.detodoprogramacion.com


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

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

La forma de parar la ejecucin


o
para indicar un fallo es elevar
una excepcin
o

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

www.detodoprogramacion.com

CAP
ITULO 9. PRUEBAS UNITARIAS

200

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. L
nea 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. L
nea 2: Como en el ejemplo anterior, la prueba es un mtodo de la clase que
e
tenga un nombre que comience por test.
3. L
nea 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.

www.detodoprogramacion.com


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 roma ntes t2 . 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.

www.detodoprogramacion.com

CAP
ITULO 9. PRUEBAS UNITARIAS

202

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. L
nea 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. L
nea 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 roma ntes t2 . 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. L
nea 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. L
nea 13: Es evidente que la funcin to roman() no eleva la excepcin OutOo
o
fRangeError que acabas de denir, puesto que an no se lo has dicho. Son
u

www.detodoprogramacion.com


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 =
for 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

www.detodoprogramacion.com

CAP
ITULO 9. PRUEBAS UNITARIAS

204

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. L
nea 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. L
nea 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. L
nea 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:

www.detodoprogramacion.com

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 roma ntes t3 . 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 roma ntes t3 . 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 =
for 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

www.detodoprogramacion.com

CAP
ITULO 9. PRUEBAS UNITARIAS

206

fcil de leer. Esta l


a
nea de cdigo deber capturar todas las entradas que sean
o
a
demasiado grandes, negativas o cero.
2. L
nea 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. L
nea 2: Oh! qu mal.
e
2. L
nea 4: Oh! incluso peor. Ambos casos deber lanzar una excepcin. En
an
o
vez de ello, retornan valores falsos.

www.detodoprogramacion.com


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 roma ntes t4 . 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

www.detodoprogramacion.com

CAP
ITULO 9. PRUEBAS UNITARIAS

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

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. L
nea 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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 9. PRUEBAS UNITARIAS

210

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 roma ntes t5 . 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

======================================================================
ERROR: t e s t r o u n d t r i p ( main . RoundtripCheck )
from roman ( to roman ( n))==n fo 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 roma ntes t5 . 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

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.

www.detodoprogramacion.com

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

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 roma ntes t5 . 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

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

Traceback ( most r e c e n t c a l l l a s t ) :
F i l e roma ntes t5 . 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

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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

9.7. MAS ENTRADAS ERRONEAS

9.7.

213

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

www.detodoprogramacion.com

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
for 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
for 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
for 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?).

www.detodoprogramacion.com

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

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 roma ntes t6 . 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

======================================================================
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 roma ntes t6 . 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 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

Traceback ( most r e c e n t c a l l l a s t ) :
F i l e roma ntes t6 . 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

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

www.detodoprogramacion.com

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 }
# tho 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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

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. L
nea 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 fo 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 roma ntes t8 . 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 ]

www.detodoprogramacion.com


10.1. INMERSION

219

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

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

www.detodoprogramacion.com

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.

www.detodoprogramacion.com

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

221

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

www.detodoprogramacion.com

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. L
nea 24: La denicin de demasiados caracteres repetidos tambin ha camo
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. L
nea 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

www.detodoprogramacion.com

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 roma ntes t9 . 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 roma ntes t9 . 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 roma ntes t9 . 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)

www.detodoprogramacion.com

224

CAP
ITULO 10. REFACTORIZAR

1. L
nea 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. L
nea 7: La prueba de valores conocidos de to roman() falla en cuanto encuentra 4000, puesto que to roman() an piensa que este valor est fuera de
u
a
rango.
3. L
nea 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).

www.detodoprogramacion.com

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 =
for 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

www.detodoprogramacion.com

CAP
ITULO 10. REFACTORIZAR

226

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 fo 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.

www.detodoprogramacion.com


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

www.detodoprogramacion.com

CAP
ITULO 10. REFACTORIZAR

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

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

www.detodoprogramacion.com


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.

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 =
for 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
for 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().

www.detodoprogramacion.com

CAP
ITULO 10. REFACTORIZAR

230

2. L
nea 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. L
nea 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. L
nea 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. L
nea 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.
, ,

www.detodoprogramacion.com

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

www.detodoprogramacion.com

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.

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 11. FICHEROS

234

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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 11. FICHEROS

236

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. L
nea 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. L
nea 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. L
nea 2: Una vez has abierto el chero (con la codicacin de caracteres coo
rrecta) para leer de l hay que llamar al mtodo read() del objeto de ujo. El
e
e
resultado es una cadena de texto.

www.detodoprogramacion.com

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.

www.detodoprogramacion.com

CAP
ITULO 11. FICHEROS

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

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. L
nea 1: Muvete al byte 18 e intenta leer un carcter.
e
a
2. L
nea 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

que es un objeto de ujo o stream

www.detodoprogramacion.com

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

239

# 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

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.

www.detodoprogramacion.com

CAP
ITULO 11. FICHEROS

240
1
2
3
4

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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 11. FICHEROS

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

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:

Simplemente abre el chero y


comienza a escribir.

Modo de escritura que sobreescribe el


chero. Se debe pasar el parmetro moa
de=w a la funcin open().
o

www.detodoprogramacion.com

11.3. ESCRIBIR EN FICHEROS DE TEXTO

243

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
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 su cc ee de dan d a g a i n

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,

www.detodoprogramacion.com

CAP
ITULO 11. FICHEROS

244

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

www.detodoprogramacion.com

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.

www.detodoprogramacion.com

246

11.5.

CAP
ITULO 11. FICHEROS

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.

www.detodoprogramacion.com

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.

www.detodoprogramacion.com

248

11.5.1.

CAP
ITULO 11. FICHEROS

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

rwr r

1. L
nea 3: Los cheros comprimidos se deben abrir siempre en modo binario
(Observa el carcter b en el parmetro mode).
a
a
2. L
nea 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. L
nea 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. L
nea 11: El comando cat muestra el contenido de un chero. Este chero con-

www.detodoprogramacion.com


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.

www.detodoprogramacion.com

CAP
ITULO 11. FICHEROS

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

1. L
nea 2: La funcin print() en un bucle. Nada sorprendente en este trozo de
o
cdigo.
o
2. L
nea 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. L
nea 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.

www.detodoprogramacion.com


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

251

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

def

e x i t ( s e l f , args ) :
sys . stdout = s e l f . out old

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

SyntaxError : i n v a l i d s y n t ax

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.

www.detodoprogramacion.com

CAP
ITULO 11. FICHEROS

252
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 ) \
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

def

e x i t ( s e l f , args ) :
sys . stdout = s e l f . out old

1. L
nea 2: El mtodo init () se llama inmediatamente despus de que la inse
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

www.detodoprogramacion.com


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.

www.detodoprogramacion.com

CAP
ITULO 11. FICHEROS

254

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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 12. XML

256

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 >

www.detodoprogramacion.com


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

www.detodoprogramacion.com

CAP
ITULO 12. XML

258

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. L
nea 1: Esta es la etiqueta de inicio del elemento foo.
2. L
nea 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 ra Un


z.
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. L
nea 1: El elemento foo tiene un atributo denominado lang. El valor del
atributo lang es en.
2. L
nea 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.

www.detodoprogramacion.com


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.

www.detodoprogramacion.com

CAP
ITULO 12. XML

260

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 ra que toda fuente Atom comparte:
a
z,
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 >

www.detodoprogramacion.com


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

www.detodoprogramacion.com

CAP
ITULO 12. XML

262

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. L
nea 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. L
nea 6: El elemento title indica el t
tulo del art
culo. Dive into history, 2009
edition.
3. L
nea 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. L
nea 10: Las entradas, como la fuente, necesitan un identicador unico.

5. L
nea 11: Las entradas tienen dos fechas: la fecha de primera publicacin
o
(published) y la fecha de ultima modicacin (updated).

o
6. L
nea 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. L
nea 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

www.detodoprogramacion.com


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

www.detodoprogramacion.com

CAP
ITULO 12. XML

264

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. L
nea 2: Continuando con el ejemplo anterior, el elemento ra es {http://www.w3.org/2005/Atom
z
2. L
nea 4: La longitud del elemento ra es el nmero de elementos hijo.
z
u
3. L
nea 6: Puedes utilizar el elemento como iterador para recorrer todos los
elementos hijo.
4. L
nea 7: Como ves por la salida, existen ocho elementos hijos: todos los metadatos 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.

www.detodoprogramacion.com


12.5. BUSQUEDA DE NODOS EN UN DOCUMENTO XML

12.4.2.

265

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

www.detodoprogramacion.com

CAP
ITULO 12. XML

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

>>> 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. L
nea 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. L
nea 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. L
nea 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. L
nea 1: Por comodidad, el objeto tree (devuelto por la funcin etree.parse() tieo
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

www.detodoprogramacion.com


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

www.detodoprogramacion.com

CAP
ITULO 12. XML

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

>>> 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. L
nea 1: Esta consulta //http://www.w3.org/2005/Atomlink es muy similar 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. L
nea 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. L
nea 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

www.detodoprogramacion.com

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

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

www.detodoprogramacion.com

CAP
ITULO 12. XML

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

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. L
nea 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. L
nea 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. L
nea 8: La consulta encuentra todos los elementos Atom con el atributo href
cuyo valor sea http://diveintomark.org/.
4. L
nea 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

www.detodoprogramacion.com


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 h ar sh 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

www.detodoprogramacion.com

CAP
ITULO 12. XML

272
1
2
3
4
5

>>> 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. L
nea 2: Para crear un elemento nuevo, se debe instanciar un objeto de la clase 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. L
nea 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. L
nea 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

www.detodoprogramacion.com


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

www.detodoprogramacion.com

CAP
ITULO 12. XML

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

>>> 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. L
nea 1: Para crear elementos hijo de un elemento existente, instancia objetos 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. L
nea 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. L
nea 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. L
nea 6: Para establecer el contenido de texto de un elemento basta con asignarle valor a la propiedad .text.
5. L
nea 7: Ahora el elemento title se serializa con su contenido de texto. Cualquier contenido de texto que contenga s
mbolos menor que o ampersands
necesitan escaparse al serializarse. lxml hace estas conversiones de forma automtica.
a
6. L
nea 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

www.detodoprogramacion.com


12.8. ANALISIS DE XML ESTROPEADO

12.8.

275

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

www.detodoprogramacion.com

CAP
ITULO 12. XML

276

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 rs 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. L
nea 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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 12. XML

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

www.detodoprogramacion.com

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

www.detodoprogramacion.com


CAP
ITULO 13. SERIALIZACION DE OBJETOS EN PYTHON

280

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

www.detodoprogramacion.com

13.2. ALMACENAMIENTO DE DATOS A UN FICHERO PICKLE

281

1 >>> s h e l l = 2

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 om me nt s li nk ] = 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

www.detodoprogramacion.com

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. L
nea 1: Seguimos en la consola #1.
2. L
nea 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. L
nea 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.

www.detodoprogramacion.com

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 mm en ts li nk : 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.

www.detodoprogramacion.com

284

CAP
ITULO 13. SERIALIZACION DE OBJETOS EN PYTHON

5. L
nea 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. L
nea 1: Volvemos a la consola #1.
2. L
nea 3: Abrimos el chero entry.pickle.
3. L
nea 4: Cargamos los datos serializados en la nueva variable entry2.
4. L
nea 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. L
nea 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.

www.detodoprogramacion.com


13.4. SERIALIZACION CON PICKLE SIN PASAR POR UN FICHERO

13.4.

285

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

www.detodoprogramacion.com


CAP
ITULO 13. SERIALIZACION DE OBJETOS EN PYTHON

286

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

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 .

rwr r

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.

www.detodoprogramacion.com


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

www.detodoprogramacion.com

287


CAP
ITULO 13. SERIALIZACION DE OBJETOS EN PYTHON

288
1
2
3
4
5
6
7
8
9
10

256: q
258: X

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 pc o de 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

www.detodoprogramacion.com

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

http://json.org/
http://www.ietf.org/rfc/rfc4627.txt
6
http://www.ietf.org/rfc/rfc4627.txt
5

www.detodoprogramacion.com


CAP
ITULO 13. SERIALIZACION DE OBJETOS EN PYTHON

290

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 [ co mm e nts li 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. L
nea 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. L
nea 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. L
nea 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 ] ,
c o m m en ts li nk : n u l l , i d : 2 5 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 }

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)

www.detodoprogramacion.com

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
],
c o mm en ts li nk : n u l l ,
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

www.detodoprogramacion.com

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 en ts li nk : 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. L
nea 3: Ok, es el momento de volver a la estructura de datos entry. Tiene de

www.detodoprogramacion.com


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

www.detodoprogramacion.com

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. L
nea 3: El mdulo customserializer es el lugar en el que has denido la funcin
o
o
to json() del ejemplo anterior.
2. L
nea 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. L
nea 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. L
nea 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

www.detodoprogramacion.com


13.10. SERIALIZACION DE TIPOS NO SOPORTADOS EN JSON
1
2
3
4
5
6
7
8
9
10

295

import time
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 } ,
c o mm en ts li nk : n u l l , i n t e r n a l i d : { c l a s s : b y t e s ,
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

www.detodoprogramacion.com

296

13.11.

CAP
ITULO 13. SERIALIZACION DE OBJETOS EN PYTHON

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 en ts li nk : 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. L
nea 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. L
nea 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. L
nea 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

www.detodoprogramacion.com

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:

www.detodoprogramacion.com

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 en ts li nk : 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. L
nea 5: Para utilizar la funcin from json() durante el proceso de deserializao
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. L
nea 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. L
nea 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

www.detodoprogramacion.com

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/

www.detodoprogramacion.com

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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 14. SERVICIOS WEB HTTP

302

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.
1

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

www.detodoprogramacion.com

14.2. CARACTER
ISTICAS DE HTTP

14.2.1.

303

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.

www.detodoprogramacion.com

CAP
ITULO 14. SERVICIOS WEB HTTP

304

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 podr haber cambiado,
a
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
11

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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

CAP
ITULO 14. SERVICIOS WEB HTTP

306

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

www.detodoprogramacion.com

14.2. CARACTER
ISTICAS DE HTTP

307

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

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

www.detodoprogramacion.com

CAP
ITULO 14. SERVICIOS WEB HTTP

308

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

www.detodoprogramacion.com

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

www.detodoprogramacion.com

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. L
nea 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. L
nea 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. L
nea 5: La primera l
nea especica el verbo HTTP que ests utilizando y el
a
camino al recurso (menos el nombre de dominio).
4. L
nea 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

www.detodoprogramacion.com


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

www.detodoprogramacion.com

312

CAP
ITULO 14. SERVICIOS WEB HTTP

7. L
nea 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!

www.detodoprogramacion.com


14.5. INTRODUCCION A HTTPLIB2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

313

# 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

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.

www.detodoprogramacion.com

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.

www.detodoprogramacion.com


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

www.detodoprogramacion.com

CAP
ITULO 14. SERVICIOS WEB HTTP

316

al crear el objeto Http. No es necesario que el directorio exista, httplib2 lo


crear si es necesario.
a
2. L
nea 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. L
nea 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

www.detodoprogramacion.com


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

www.detodoprogramacion.com

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. L
nea 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. L
nea 3: El HTTP status vuelve a ser 200, como antes.
3. L
nea 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. L
nea 5: Activamos la depuracin pa ver lo que est sucediendo en la comunio
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. L
nea 6: Se crea un objeto httplib2.Http con el mismo nombre de directorio
que antes.
3. L
nea 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.

www.detodoprogramacion.com


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.

www.detodoprogramacion.com

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. L
nea 4: httplib2 te permite aadir cabeceras HTTP a cualquier peticin san
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. L
nea 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. L
nea 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.

www.detodoprogramacion.com


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.

www.detodoprogramacion.com

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. L
nea 4: En lugar del uo, esta vez vamos a descargar la pgina de inicio de
a
la sede web, que es HTML.
2. L
nea 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. L
nea 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.

www.detodoprogramacion.com


14.5. INTRODUCCION A HTTPLIB2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

323

# 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

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.

www.detodoprogramacion.com

324

CAP
ITULO 14. SERVICIOS WEB HTTP

8. L
nea 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. L
nea 5: Cada vez que httplib2 env una peticin, incluye una cabecera Accepta
o
Encoding para indicarle al servidor que puede manipular compresiones gzip o
deate.
2. L
nea 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].

www.detodoprogramacion.com


14.5. INTRODUCCION A HTTPLIB2

14.5.5.

325

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

www.detodoprogramacion.com

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. L
nea 2: La respuesta que se obtiene en esta llamada, response del mtodo
e
request() es la respuesta de la URL nal.
2. L
nea 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. L
nea 12: Por cierto, este ujo est comprimido.
a
4. L
nea 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.

www.detodoprogramacion.com


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

www.detodoprogramacion.com

CAP
ITULO 14. SERVICIOS WEB HTTP

328

2. L
nea 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. L
nea 9: De nuevo, el servidor responde con un cdigo de estado 302. Peo
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. L
nea 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. L
nea 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. L
nea 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. L
nea 10: httplib2 sigui la redireccin desde la cach.
o
o
e
Pero espera, que hay ms!
a

www.detodoprogramacion.com

14.6. UN PASO MAS ALLA DE HTTP GET


1
2
3
4
5
6
7

329

# 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

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

www.detodoprogramacion.com

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. L
nea 1: Python dispone de una funcin de utiliza para codicar en este foro
mato cualquier diccionario: urllib.parse.urlencode().
2. L
nea 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. L
nea 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

www.detodoprogramacion.com

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:

www.detodoprogramacion.com

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. L
nea 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. L
nea 11: httplib2 inmediatamente vuelve a pedir la URL por segunda vez.
3. L
nea 16: Esta vez, incluye el cdigo de usuario y clave de acceso que aadiste
o
n
en el mtodo add credentials().
e
4. L
nea 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

www.detodoprogramacion.com

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

333

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

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.

www.detodoprogramacion.com

CAP
ITULO 14. SERVICIOS WEB HTTP

334

3. L
nea 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. L
nea 3: El servidor retorn XML correcto? Ya sabes cmo analizar un docuo
o
mento XML.
2. L
nea 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. L
nea 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:

www.detodoprogramacion.com

14.8. LECTURAS RECOMENDADAS

335

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

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.

Lecturas recomendadas

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

www.detodoprogramacion.com

CAP
ITULO 14. SERVICIOS WEB HTTP

336

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

www.detodoprogramacion.com

Libros Universitarios, juegos, Revistas, Cursos, Software, Sistemas


Operativos, Antivirus y ms Gratis para el Conocimiento...!
www.detodoprogramas.com
Vistanos y comprubalo

Material para los amantes de la Programacin Java,


C/C++/C#,Visual.Net, SQL, Python, Javascript, Oracle, Algoritmos,
CSS, Desarrollo Web, Joomla, jquery, Ajax y Mucho Mas
www.detodoprogramacion.com
Visitanos

S-ar putea să vă placă și