Beruflich Dokumente
Kultur Dokumente
www.detodoprogramacion.com
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:
www.detodoprogramacion.com
www.detodoprogramacion.com
Cap
tulo -1
Novedades de Inmersin en
o
Python 3
No es de aqu de donde venimos?
-1.1.
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
www.detodoprogramacion.com
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
0.2.
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
www.detodoprogramacion.com
0.3. INSTALACION EN MICROSOFT WINDOWS
0.3.
o
disponible de Python 3.x a menos que tengas alguna razn importante para no
o
hacerlo.
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
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
www.detodoprogramacion.com
0.3. INSTALACION EN MICROSOFT WINDOWS
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
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
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
0.4.
Instalacin en un Mac OS X
o
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
www.detodoprogramacion.com
14
CAP
ITULO 0. INSTALACION DE PYTHON
Haz doble click con el cursor sobre el chero de instalacin Python.mpkg para
o
iniciar el instalador de Python para Mac.
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
www.detodoprogramacion.com
16
CAP
ITULO 0. INSTALACION DE PYTHON
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.
www.detodoprogramacion.com
0.4. INSTALACION EN UN MAC OS X
17
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
www.detodoprogramacion.com
0.5. INSTALACION EN UBUNTU LINUX
19
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.
www.detodoprogramacion.com
20
CAP
ITULO 0. INSTALACION DE PYTHON
www.detodoprogramacion.com
0.5. INSTALACION EN UBUNTU LINUX
21
www.detodoprogramacion.com
22
CAP
ITULO 0. INSTALACION DE PYTHON
www.detodoprogramacion.com
0.5. INSTALACION EN UBUNTU LINUX
23
www.detodoprogramacion.com
24
CAP
ITULO 0. INSTALACION DE PYTHON
www.detodoprogramacion.com
0.6. INSTALACION EN OTRAS PLATAFORMAS
25
0.6.
0.7.
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!
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
27
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.
>>>
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.
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
29
11
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
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 ) )
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
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
o
ejemplo:
1
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
1.2.1.
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
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
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
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.
1.3.1.
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
1.4.
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
39
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.
>>>
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
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
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
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
0.
4. Lnea 4: Esta l
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.
Excepciones
www.detodoprogramacion.com
1.7. EXCEPCIONES
43
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.
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
i f chardet :
# hacer algo
else :
# s e g u i r de t o d o s modos
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
www.detodoprogramacion.com
45
tiene que estar repleto de sentencias if para llamar a diferentes mdulos con diferente
o
nombre.
1.8.
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
1.9.
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
1.10.
Ejecucin de scripts
o
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 ) )
www.detodoprogramacion.com
47
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,
2.1.
Inmersin
o
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
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
>>> tamanyo
>>> tamanyo
False
>>> tamanyo
>>> tamanyo
False
>>> tamanyo
>>> tamanyo
True
= 1
< 0
= 0
< 0
= 1
< 0
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.
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
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
2.3.2.
>>>
5.5
>>>
5
>>>
6
>>>
5.0
>>>
121
>>>
1
11 / 2
11 // 2
11
// 2
1 1 . 0 // 2
11 2
11 % 2
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
>>> 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
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.
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
www.detodoprogramacion.com
2.4. LISTAS
2.4.
57
Listas
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 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
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
2.4.2.
Particin de listas
o
>>>
[ 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
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
2.4.3.
>>> 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 ]
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
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
>>> 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
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
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
2.4.4.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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,
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.
1. L
nea 4: Para eliminar un elemento de una lista puedes utilizar la sentencia
del.
www.detodoprogramacion.com
2.4. LISTAS
63
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
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.
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
1. L
nea 2: Cuando se llama sin parmetros, el mtodo pop() elimina el ultimo
a
e
2.4.7.
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
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
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
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
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.
>>> 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 >
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
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.
>>> 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
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
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
2.6.1.
Creacin de conjuntos
o
>>> 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
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
>>>
>>>
>>>
{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 )
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
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}
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.
>>> 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
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
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
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
n
en otro conjunto ambas consultas devuelven False.
2.6.5.
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
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
2.7.1.
Creacin de diccionarios
o
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
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.
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 ] }
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
2.7.4.
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
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
www.detodoprogramacion.com
2.8.1.
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
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.
Sistema Operativo.
83
www.detodoprogramacion.com
CAP
ITULO 3. COMPRENSIONES
84
3.2.1.
>>> 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
85
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.
>>> 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
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
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.
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
87
3.2.3.
Listar directorios
>>> 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 ]
www.detodoprogramacion.com
CAP
ITULO 3. COMPRENSIONES
88
3.2.4.
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
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.
3.3.
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
www.detodoprogramacion.com
CAP
ITULO 3. COMPRENSIONES
92
3.4.
>>> 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
1. Lnea 4: Este ejemplo construye una lista con todos los cheros del directorio
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
3.4.1.
3.5.
e
o
muy similiar a la de los diccionarios, con la unica diferencia de que unicamente se
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.
95
www.detodoprogramacion.com
96
CAP
ITULO 4. CADENAS DE TEXTO
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
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
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
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
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
>>> 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
101
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
el operador +.
4.4.
Formatear cadenas
Las cadenas de texto se pueden denir utilizando comillas simples o dobles.
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
103
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.
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
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:
www.detodoprogramacion.com
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
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
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.
. . . 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
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
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
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
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
109
ndice de la parte derecha de la cadena coincide con la longitud de la cadena, puedes omitirlo. As que una cadena[23:]
4.6.
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
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
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
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
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
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.
Python 3 asume que el cdigo fuente cada chero .py est codicado en
o
a
UTF-8.
www.detodoprogramacion.com
113
c o d i n g : windows 1252
4.8.
Lecturas recomendadas
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
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.
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
117
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
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
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
www.detodoprogramacion.com
CAP
ITULO 5. EXPRESIONES REGULARES
118
1
2
3
4
5
6
7
8
9
10
11
12
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
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.
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).
www.detodoprogramacion.com
CAP
ITULO 5. EXPRESIONES REGULARES
120
5.3.1.
.
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
www.detodoprogramacion.com
5.3. CASO DE ESTUDIO: NUMEROS ROMANOS
121
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.
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
>>> 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
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
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
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.
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
>>> 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
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.
o
opcional, luego CM, luego XL y termina detectando el nal de la cadena.
Recuerda que la sintaxis (ABC) signica que se encuentre unicamente
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 ? ) $
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
127
5.5.
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
www.detodoprogramacion.com
CAP
ITULO 5. EXPRESIONES REGULARES
128
>>> 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
129
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.
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
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
www.detodoprogramacion.com
131
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
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
133
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
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
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 , )
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
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.
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
www.detodoprogramacion.com
CAP
ITULO 6. CIERRES Y GENERADORES
138
6.2.
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
,
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
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.
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.
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
www.detodoprogramacion.com
CAP
ITULO 6. CIERRES Y GENERADORES
142
6.3.
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 )
www.detodoprogramacion.com
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
u
retorna True siempre, lo que signica que se aplicar en ultima instancia la
a
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 )
www.detodoprogramacion.com
CAP
ITULO 6. CIERRES Y GENERADORES
144
6.4.
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
145
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
,
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 )
6.5.
Un chero de patrones
www.detodoprogramacion.com
[ 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
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 ))
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
www.detodoprogramacion.com
CAP
ITULO 6. CIERRES Y GENERADORES
148
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
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.
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
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]
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
6.8.
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.
153
Lecturas recomendadas
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
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
c l a s s Fib :
7.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 ()
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 ) :
www.detodoprogramacion.com
CAP
ITULO 7. CLASES E ITERADORES
158
7.3.
Instanciar clases
>>> 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
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.
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
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 ().
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.
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
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
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.
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 = [ ]
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
def
iter ( self ):
s e l f . cache index = 0
return s e l f
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
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
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
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.
www.detodoprogramacion.com
7.7.
169
Lecturas recomendadas
www.detodoprogramacion.com
170
CAP
ITULO 7. CLASES E ITERADORES
www.detodoprogramacion.com
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
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
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 )
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.
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
8.3.
>>> 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
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
e
simplemente una secuencia de caracteres.
3. Lnea 8: Dada una lista de cadenas, .join(a list) concatena todas las cadenas
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 ) )
8.4.
Hacer aserciones
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
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
www.detodoprogramacion.com
8.6. CALCULO DE PERMUTACIONES... DE FORMA PEREZOSA!
177
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
8.6.
www.detodoprogramacion.com
178
CAP
ITULO 8. ITERADORES AVANZADOS
>>> 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
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
>>> 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 ) ]
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
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
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,
www.detodoprogramacion.com
CAP
ITULO 8. ITERADORES AVANZADOS
182
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
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 }
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
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 ) ) )
www.detodoprogramacion.com
184
8.7.
CAP
ITULO 8. ITERADORES AVANZADOS
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
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.
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
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 )
, , , ]
>>> 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 ) )
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
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
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 ) , { } , { } )
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
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 } , { } )
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
www.detodoprogramacion.com
CAP
ITULO 8. ITERADORES AVANZADOS
190
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.
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
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.
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
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:
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
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
www.detodoprogramacion.com
196
CAP
ITULO 9. PRUEBAS UNITARIAS
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
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
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 )
)
...
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.
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
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
www.detodoprogramacion.com
9.3. PARA Y PRENDELE FUEGO
201
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
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
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 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
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)
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
9.5.
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
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 )
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
9.6.
www.detodoprogramacion.com
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
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
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)
(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
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)
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
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.
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
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
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
# 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
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
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
o
Desde Python 3.1 puedes dejar de poner los valors numricos cuando utilizas
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 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.
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
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
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
225
# 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 ) :
.
.
.
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
10.3.
Rectorizacin
o
www.detodoprogramacion.com
10.3. RECTORIZACION
227
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
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
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
10.4.
Sumario
www.detodoprogramacion.com
232
CAP
ITULO 10. REFACTORIZAR
un bala de plata. Escribir casos de prueba utiles es una tarea dura y mantenerlos
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.
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 )
www.detodoprogramacion.com
CAP
ITULO 11. FICHEROS
234
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
11.2.1.
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
235
La codicacin de caracteres
o
por defecto es dependiente de
la plataforma.
por defecto fuera otra, como por
11.2.2.
www.detodoprogramacion.com
CAP
ITULO 11. FICHEROS
236
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.
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
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
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?
# 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
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 ( )
www.detodoprogramacion.com
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
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.
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
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.
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
241
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 ( ) ) )
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
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
11.3.
www.detodoprogramacion.com
243
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
www.detodoprogramacion.com
CAP
ITULO 11. FICHEROS
244
l
nea.
11.3.1.
11.4.
Ficheros binarios
1
2
3
4
5
6
7
8
9
www.detodoprogramacion.com
245
1. Lnea 1: Abrir un chero en modo binario es simple pero sutil. La unica dife
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
# 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
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
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
>>> 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
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
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
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
11.6.
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.
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
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
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
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 )
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
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 )
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
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
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
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 & ; 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& ; 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.
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 >
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
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 >
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 >
e
http://www.w3.org/2005/Atom.
www.detodoprogramacion.com
CAP
ITULO 12. XML
260
12.3.
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
www.detodoprogramacion.com
12.3. LA ESTRUCTURA DE UNA FUENTE DE INFORMACION ATOM
261
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
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
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
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 & ; 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& ; 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 (— y …) 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
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
12.4.1.
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
# 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
diccionario vac
o.
12.5.
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
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
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.
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
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
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
www.detodoprogramacion.com
269
12.6.
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
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.
www.detodoprogramacion.com
12.7. GENERACION DE XML
1
2
3
4
5
6
7
8
9
10
271
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.
12.7.
Generacin de XML
o
www.detodoprogramacion.com
CAP
ITULO 12. XML
272
1
2
3
4
5
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
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 />
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
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
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
http://github.com/galvez/xmlwitch/tree/master
www.detodoprogramacion.com
12.8. ANALISIS DE XML ESTROPEADO
12.8.
275
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
Eso es un error, porque la entidad … 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. 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
277
este objeto parser como segundo parmetro de la funcin parse(). Observa que
a
o
lxml no eleva ninguna excepcin sobre la entidad no denida ….
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 …, el
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 … de HTML y reemplazarla por &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
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
13.1.1.
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
http://docs.python.org/3.1/library/sys.html#sys.getrecursionlimit
www.detodoprogramacion.com
281
1 >>> s h e l l = 2
13.2.
>>>
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)
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
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.
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
283
o
u
el chero en modo binario o los datos se corrompern durante la escritura.
a
13.3.
>>> 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 }
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
>>> 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
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.
www.detodoprogramacion.com
CAP
ITULO 13. SERIALIZACION DE OBJETOS EN PYTHON
286
13.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$ 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
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
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
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.
www.detodoprogramacion.com
289
13.8.
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 }
www.detodoprogramacion.com
291
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.
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.
>>> 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 )
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
www.detodoprogramacion.com
294
CAP
ITULO 13. SERIALIZACION DE OBJETOS EN PYTHON
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 )
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
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
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
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
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
299
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
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
14.2.
Caracter
sticas de HTTP
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
www.detodoprogramacion.com
CAP
ITULO 14. SERVICIOS WEB HTTP
304
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.
o
pgina
a
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
www.detodoprogramacion.com
14.2. CARACTER
ISTICAS DE HTTP
305
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
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
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
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.
www.detodoprogramacion.com
14.2. CARACTER
ISTICAS DE HTTP
307
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
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.
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 / />
...
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.
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
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
# 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
o
o
3. Lnea 5: La respuesta incluye una cabecera Last-Modied.
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 . . .
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
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
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
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
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
>>> 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
www.detodoprogramacion.com
CAP
ITULO 14. SERVICIOS WEB HTTP
316
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.
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.
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
# 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
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
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.
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.
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
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
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
>>>
3. Lnea 17: Esto signica que puedes hacer response.previous.previous para seguir
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
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
,
e
3. Lnea 6: S el resultado es el ujo completo (de la cach).
,
e
HTTP funciona.
14.6.
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
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
www.detodoprogramacion.com
>>>
>>>
>>>
>>>
>>>
>>>
>>>
...
...
...
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 } )
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
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
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.
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
www.detodoprogramacion.com
335
e
3. Lnea 8: No tengo permiso? Soy yo, borra el mensaje, por favor...
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
www.detodoprogramacion.com