Python para Informáticos
Python para Informáticos
Charles Severance
Version 2.7.2
Prefacio
Python para informáticos: Remezclando un libro libre
¿Por qué debería aprender a escribir programas?
Creatividad y motivación
Arquitectura hardware del PC
Comprendiendo la programación
Palabras y frases
Conversando con Python
Terminología: intérprete y compilador
Escribir un programa
¿Qué es un programa?
Los bloques de construcción de los programas
¿Qué es posible que vaya mal?
El viaje de aprendizaje
Glosario
Ejercicios
Variables, expresiones y sentencias
Valores y tipos
Variables
Nombres de variables y palabras claves
Sentencias
Operadores y operandos
Expresiones
Orden de las operaciones
Operador módulo
Operaciones con cadenas
Petición de información al usuario
Comentarios
Elección de nombres de variables mnemónicos
Depuración
Glosario
Ejercicios
Ejecución condicional
Expresiones booleanas
Operadores lógicos
Ejecución condicional
Ejecución alternativa
Condicionales encadenados
Condicionales anidados
Captura de excepciones usando try y except
Evaluación en cortocircuito de expresiones lógicas
Depuración
Glosario
Ejercicios
Funciones
Llamadas a funciones
Funciones internas
Funciones de conversión de tipos
Números aleatorios
Funciones matemáticas
Añadiendo funciones nuevas
Definición y usos
Flujo de ejecución
Parámetros y argumentos
Funciones productivas y funciones estériles
¿Por qué funciones?
Depuración
Glosario
Ejercicios
Iteración
Actualización de variables
La sentencia while
Bucles infinitos
“Bucles infinitos” y break
Finalizar iteraciones con continue
Bucles definidos usando for
Diseños de bucles
Depuración
Glosario
Ejercicios
Cadenas
Una cadena es una secuencia
Obtener la longitud de una cadena mediante len
Recorrido a través de una cadena con un bucle
Rebanado de cadenas
Las cadenas son inmutables
Bucles y contadores
El operador in
Comparación de cadenas
Métodos de cadenas
Análisis de cadenas
Operador de formato
Depuración
Glosario
Ejercicios
Ficheros
Persistencia
Apertura de ficheros
Ficheros de texto y líneas
Lectura de ficheros
Búsqueda dentro de un fichero
Permitir al usuario elegir el nombre del fichero
Uso de try, except, y open
Escritura en ficheros
Depuración
Glosario
Ejercicios
Listas
Una lista es una secuencia
Las listas son mutables
Recorrer una lista
Operaciones con listas
Rebanado de listas
Métodos de listas
Borrado de elementos
Listas y funciones
Listas y cadenas
Análisis de líneas
Objetos y valores
Alias
Listas como argumentos
Depuración
Glosario
Ejercicios
Diccionarios
Diccionario como conjunto de contadores
Diccionarios y archivos
Bucles y diccionarios
Procesado avanzado de texto
Depuración
Glosario
Ejercicios
Tuplas
Las tuplas son inmutables
Comparación de tuplas
Asignación de tuplas
Diccionarios y tuplas
Asignación múltiple con diccionarios
Las palabras más comunes
Uso de tuplas como claves en diccionarios
Secuencias: cadenas, listas, y tuplas—¡Oh, Dios mío!
Depuración
Glosario
Ejercicios
Expresiones regulares
Coincidencia de caracteres en expresiones regulares
Extracción de datos usando expresiones regulares
Combinar búsqueda y extracción
Escapado de caracteres
Resumen
Sección extra para usuarios de Unix
Depuración
Glosario
Ejercicios
Programas en red
Protocolo de Transporte de Hipertexto - HTTP
El Navegador Web Más Sencillo del Mundo
Recepción de una imagen mediante HTTP
Recepción de páginas web con urllib
Análisis de HTML y rascado de la web
Análisis de HTML mediante expresiones regulares
Análisis de HTML mediante BeautifulSoup
Lectura de archivos binarios mediante urllib
Glosario
Ejercicios
Uso de servicios web
eXtensible Markup Language - XML
Análisis de XML
Desplazamiento a través de los nodos
JavaScript Object Notation - JSON
Análisis de JSON
Interfaces de programación de aplicaciones
Servicio web de geocodificación de Google
Seguridad y uso de APIs
Glosario
Ejercicios
Bases de datos y SQL
¿Qué es una base de datos?
Conceptos sobre bases de datos
Add-on de Firefox para gestión de SQLite
Creación de una tabla en una base de datos
Resumen de Lenguaje de Consultas Estructurado
Rastreo en Twitter usando una base de datos
Modelado de datos básico
Programación con múltiples tablas
Tres tipos de claves
Uso de JSON para recuperar datos
Resumen
Depuración
Glosario
Visualización de datos
Construcción de un mapa de Google a partir de datos
geocodificados
Visualización de redes e interconexiones
Visualización de datos de correo
Automatización de tareas habituales en tu PC
Nombres de archivo y rutas
Ejemplo: Limpieza de un directorio de fotos
Argumentos de línea de comandos
Pipes (tuberías)
Glosario
Ejercicios
Programando con Python en Windows
Programando con Python en Macintosh
Colaboraciones
Lista de colaboradores de “Python para Informáticos”
Prefacio para “Think Python”
Lista de colaboradores de “Think Python”
Detalles del Copyright
Index
Los capítulos 2–10 son similares a los del libro Think Python, pero en ellos
hay cambios importantes. Los ejemplos y ejercicios dedicados a números
han sido reemplazados por otros orientados a datos. Los temas se presentan
en el orden adecuado para ir construyendo soluciones de análisis de datos
progresivamente más sofisticadas. Algunos temas, como try y except, se
han adelantado y son presentados como parte del capítulo de condicionales.
Las funciones se tratan muy someramente hasta que se hacen necesarias para
manejar programas complejos, en vez de introducirlas en las primeras
lecciones como abstracción. Casi todas las funciones definidas por el
usuario han sido eliminadas del código de los ejemplos y ejercicios, excepto
en el capítulo 4. La palabra “recursión”1 no aparece en todo el libro.
Creo que este libro sirve como ejemplo de por qué los materiales libres son
tan importantes para el futuro de la educación, y quiero agradecer a Allen B.
Downey y al servicio de publicaciones de la Universidad de Cambridge por
su amplitud de miras al permitir que este libro esté disponible con unos
derechos de reproducción abiertos. Espero que estén satisfechos con el
resultado de mis esfuerzos y deseo que tú como lector también estés
satisfecho con nuestros esfuerzos colectivos.
Charles Severance
www.dr-chuck.com
Ann Arbor, MI, USA
9 de Septiembre de 2013
1
Excepto, por supuesto, en esta línea.
Chapítulo 1 ¿Por qué debería aprender a escribir
programas?
Escribir programas (o programar) es una actividad muy gratificante y
creativa. Puedes escribir programas por muchas razones, desde por
mantenerte activo hasta por resolver un problema difícil de análisis de datos
o por divertirte ayudando a otros a resolver cualquier cuestión. Este libro
asume que todo el mundo necesita saber programar, y que una vez que sepas
programar ya encontrarás tú mismo la forma de aplicar tus recién adquiridas
habilidades.
Por ejemplo, echa un vistazo a los primeros tres párrafos de este capítulo y
dime cual es la palabra más utilizada y cuántas veces se ha usado. A pesar
de que seas capaz de leer y comprender las palabras en unos pocos
segundos, contarlas cuesta más, porque no es el tipo de problema que las
mentes humanas fueron diseñadas para resolver. Para un PC1 es justo al
revés: leer y comprender texto de un trozo de papel es algo complicado para
él, pero contar las palabras y decir cuántas veces se ha usado la más
frecuente le resulta muy sencillo:
python words.py
Introduce fichero:words.txt
que 8
El hecho de que los PCs sean buenos en cosas en las que los humanos no lo
son es el motivo por el que necesitas ser capaz de hablar “idioma de PC”.
Una vez que hayas aprendido ese nuevo idioma, podrás delegar tareas
mundanas en tu socio (la máquina), dejando más tiempo libre para ti, de
modo que puedas dedicarte a aquellas otras cosas para las que estás más
capacitado. Serás el encargado de poner la creatividad, intuición e inventiva
a esa asociación.
Las palabras reservadas en el idioma en que los humanos hablan con Python
contiene las siguientes:
Antes de que puedas conversar con Python, debes instalar en primer lugar el
software de Python en tu equipo, y aprender a ponerlo en marcha. La
explicación sobre cómo conseguirlo excede el propósito de este capítulo, así
que te sugiero consultar www.pythonlearn.com, donde tengo instrucciones
detalladas y capturas de pantallas sobre cómo instalar y poner en marcha
Python en sistemas Macintosh y Windows5. En algún momento, terminarás en
un terminal o ventana de comandos, escribirás python, y el intérprete de
Pyhton comenzará a ejecutarse en modo interactivo, apareciendo algo como
lo siguiente:
Esto no está funcionando. A menos que pienses en algo rápido, los habitantes
del planeta probablemente te clavarán sus lanzas, te ensartarán en un asador,
te cocinarán sobre el fuego, y te usarán como cena.
Por suerte has comprado una copia de este libro durante el viaje, así que lo
hojeas hasta llegar precisamente a esta página y pruebas de nuevo:
Esto tiene mejor aspecto, así que intentas comunicarte un poco más:
^
SyntaxError: EOL while scanning string literal
>>>
>>> adios
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'adios' is not defined
>>> quit()
01010001110100100101010000001111
11100110000011101010010101101101
...
El código máquina superficialmente parece muy sencillo, dado que sólo hay
ceros y unos, pero su sintaxis es incluso más complicada y mucho más
intrincada que Python. Así que muy pocos programadores usan este lenguaje.
En vez de eso, se han construido varios traductores para permitir a los
programadores escribir en lenguajes de alto nivel, como Python o
JavaScript, y esos traductores convierten luego los programas a código
máquina para que la CPU pueda ejecutarlos.
Dado que el código máquina está ligado al hardware del equipo, ese código
no es portable a través de los diferentes tipos de hardware. Los programas
escritos en lenguajes de alto nivel pueden ser trasladados a diferentes
equipos usando un intérprete distinto en cada máquina, o recompilando el
código para crear una versión en código máquina del programa para cada
nuevo equipo.
>>> x = 6
>>> print x
6
>>> y = x * 7
>>> print y
42
>>>
^?ELF^A^A^A^@^@^@^@^@^@^@^@^@^B^@^C^@^A^@^@^@\xa0\x82
^D^H4^@^@^@\x90^]^@^@^@^@^@^@4^@ ^@^G^@(^@$^@!^@^F^@
^@^@4^@^@^@4\x80^D^H4\x80^D^H\xe0^@^@^@\xe0^@^@^@^E
^@^@^@^D^@^@^@^C^@^@^@^T^A^@^@^T\x81^D^H^T\x81^D^H^S
^@^@^@^S^@^@^@^D^@^@^@^A^@^@^@^A\^D^HQVhT\x83^D^H\xe8
....
No es fácil leer o escribir código máquina, así que está bien que tengamos
intérpretes y compiladores que nos permitan escribir en lenguajes de alto
nivel, como Python o C.
C:\Python27\python.exe
Habrás notado que no es necesario poner quit() al final del programa Python
en el archivo. Cuando Python está leyendo el código fuente desde un archivo,
sabe parar cuando alcanza el final del fichero.
mayorcantidad = None
mayorpalabra = None
for palabra,contador in contadores.items():
if mayorcantidad is None or contador >
mayorcantidad:
mayorpalabra = palabra
mayorcantidad = contador
Hay ciertos modelos conceptuales de bajo nivel que se usan para construir
programas. Estas estructuras no son exclusivas de los programas Python,
sino que son parte de cualquier lenguaje de programación, desde el código
máquina hasta los lenguajes de alto nivel.
entrada:
Obtiene datos del “mundo exterior”. Puede consistir en leer datos de un
archivo, o incluso de algún tipo de sensor, como un micrófono o GPS.
En nuestros programas iniciales la entrada vendrá del propio usuario,
escribiendo datos en el teclado.
salida:
Muestra el resultado del programa en la pantalla o lo almacena en un
archivo; o a veces lo envía a un dispositivo, como puede ser un altavoz,
para reproducir música o leer texto.
ejecución secuencial:
Ejecuta sentencias una detrás de otra, en el orden en que se encuentran
en el script.
ejecución condicional:
Comprueba ciertas condiciones y después ejecuta u omite una secuencia
de sentencias.
ejecución repetida:
Ejecuta cierto conjunto de sentencias repetidamente, normalmente con
alguna variación.
reutilización:
Se escriben un conjunto de instrucciones una vez y se las da un nombre
para después reutilizarlas cuando sean necesarias en cualquier otra
parte del programa.
Parece demasiado simple para ser verdad, y por supuesto nunca es tan
simple. Es como decir que caminar es simplemente “poner un pie delante del
otro”. El “arte” de escribir un programa es componer y entrelazar juntos
estos elementos básicos muchas veces, para producir algo que sea útil a sus
usuarios.
Hay poco que ganar discutiendo con Python. Sólo es una herramienta. No
tiene emociones, es feliz y está listo para servirte en cualquier momento que
le necesites. Sus mensajes de error parecen crueles, pero son simples
peticiones de ayuda de Python. Ha examinado lo que has escrito y
simplemente no es capaz de entender lo que has puesto.
Errores de sintaxis:
Estos son los primeros errores que cometerás y los más fáciles de
corregir. Un error de sintaxis quiere decir que has violado las reglas de
la “gramática” de Python. Python hace lo que puede para indicar la línea
y el carácter correctos en donde ha cree que está la confusión. Lo único
complicado de los errores de sintaxis es que a veces el error que se
necesita corregir está en alguna línea del programa anterior a aquella en
la cual Python emite el aviso. De modo que la línea y el carácter que
Python indica en un error de sintaxis pueden ser sólo un punto de partida
para tu investigación.
Errores lógicos:
Un error lógico es cuando tu programa tiene una sintaxis correcta, pero
existe un error en el orden de las sentencias o tal vez un error en cómo
las sentencias se relacionan unas con otras. Un buen ejemplo de un error
lógico sería, “toma un trago de tu botella de agua, ponla en tu mochila,
vete hasta la biblioteca, y luego vuelve a poner el tapón a la botella.”
Errores semánticos:
Un error semántico se produce cuando la descripción de los pasos a
seguir es sintácticamente perfecta y se realiza en el orden correcto, pero
simplemente existe un error en el programa. El programa es
perfectamente correcto, pero no realiza aquello que tú pretendías que
hiciera. Un ejemplo sencillo podría ser si tú estuvieses indicando a
alguien el camino hacia un restaurante y dijeras: “...cuando llegues a la
intersección con la gasolinera, gira a la izquierda, continúa durante
kilómetro y medio y el edificio rojo que encuentres a tu derecha será el
restaurante.” Tu amigo se retrasa y te llama para decirte que está en una
granja, dando vueltas alrededor de un granero, sin que haya señal alguna
de un restaurante. Entonces le preguntas: “¿Giraste a la izquierda o a la
derecha en la gasolinera?”, y él responde: “Seguí al pie de la letra tus
indicaciones, las tengo por escrito, y decían que debía girar la izquierda
y continuar kilómetro y medio desde la gasolinera.” Entonces le dices:
“Lo siento mucho, porque aunque mis instrucciones son sintácticamente
correctas, por desgracia contienen un pequeño e indetectado error
semántico.”.
Cuando se produce cualquiera de los tres tipos de errores, Python una vez
más está simplemente intentando por todos los medios hacer exactamente lo
que tú le has pedido.
Según vayas avanzando por el resto del libro, no te asustes si los conceptos
no parecen encajar bien unos con otros al principio. Cuando estabas
aprendiendo a hablar, no supuso un problema que durante los primeros años
sólo pudieras emitir lindos balbuceos. Y también fue normal que te llevara
seis meses pasar de un vocabulario simple a frases simples y que te llevara
5-6 años más pasar de frases a párrafos, y unos cuantos años más hasta que
fuiste capaz de escribir una historia corta interesante por ti mismo.
1.12 Glosario
bug:
Un error en un programa.
código fuente:
Un programa en un lenguaje de alto nivel.
código máquina:
El lenguaje de más bajo nivel para el software, ya que se trata del
lenguaje que es directamente ejecutado por la unidad central de
procesamiento (CPU).
compilar:
Traducir un programa escrito en un lenguaje de alto nivel a otro lenguaje
de bajo nivel de una vez, preparándolo para su posterior ejecución.
error semántico:
Un error en un programa que provoca que haga algo distinto de lo que el
programador pretendía.
interpretar:
Ejecutar un programa en un lenguaje de alto nivel traduciendo sus líneas
de una en una.
lenguaje de alto nivel:
Un lenguaje de programación como Python, que está diseñado para ser
sencillo de leer y escribir para los humanos.
lenguaje de bajo nivel:
Un lenguaje de programación que ha sido diseñado para ser sencillo de
ejecutar para una máquina; también se le llama “código máquina” o
“lenguaje ensamblador”.
memoria principal:
Almacena programas y datos. La memoria principal pierde su
información cuando se interrumpe la energía que la alimenta.
memoria secundaria:
Almacena programas y datos y retienen su información incluso cuando
la corriente se interrumpe. Generalmente es más lenta que la memoria
principal. Ejemplos de memoria secundaria pueden ser unidades de
disco y memorias flash en lápices USB.
modo interactivo:
Un modo de uso de usar el intérprete de Python escribiendo comandos y
expresiones en el prompt (indicador).
parsear:
Examinar un programa y analizar su estructura sintáctica.
portabilidad:
La propiedad de un programa que le permite funcionar en más de un tipo
de equipo.
programa:
Un conjunto de instrucciones que especifican una operación.
prompt:
Cuando un programa muestra un mensaje y se detiene para que el
usuario escriba alguna entrada para el programa.
resolución de problemas:
El proceso de formular un problema, encontrar una solución y expresar
esa solución.
semántica:
El significado de un programa.
sentencia print:
Una instrucción que provoca que el intérprete de Python muestre un
valor en la pantalla.
unidad central de procesamiento:
El corazón de cualquier PC. Es lo que ejecuta el software que
escribimos; también se le suele llamar “CPU” o “el procesador”.
1.13 Ejercicios
a) El intérprete de Python
b) El teclado
c) El código fuente de Python
d) Un documento de un procesador de texto
Ejercicio 6 ¿En qué parte del equipo queda almacenada una variable
como “X” después de que termine la siguiente línea de Python?:
x = 123
x = 43
x = x + 1
print x
a) 43
b) 44
c) x + 1
d) Error, porque x = x + 1 no es posible matemáticamente
1
Personal Computer, es decir, computadora u ordenador personal
(Nota del trad.)
2
Personal Digital Assistant (Nota del trad.)
3
http://xkcd.com/231/
4
“try” en inglés puede traducirse como “intento”(Nota del Trad.)
5
En los capítulos finales del libro también encontrarás dos apéndices
con instrucciones sobre la instalación de Python en esos sistemas (Nota
del trad.)
Chapítulo 2 Variables, expresiones y sentencias
Un valor es una de las cosas básicas que utiliza un programa, como una letra
o un número. Los valores que hemos visto hasta ahora han sido 1, 2, y
'¡Hola, mundo!'
python
>>> print 4
4
No resulta sorprendente que las cadenas pertenezca al tipo str, y los enteros
pertenezcan al tipo int. Resulta sin embargo menos obvio que los números
con un punto decimal pertenezcan a un tipo llamado float (flotante), ya que
esos números se representan en un formato conocido como punto flotante 1.
>>> type(3.2)
<type 'float'>
¿Qué ocurre con valores como '17' y '3.2'? Parecen números, pero van
entre comillas como las cadenas.
>>> type('17')
<type 'str'>
>>> type('3.2')
<type 'str'>
Son cadenas.
Cuando escribes un entero grande, puede que te sientas tentado a usar comas
o puntos para separarlo en grupos de tres dígitos, como en 1,000,000 2. Eso
no es un entero válido en Python, pero en cambio sí que resulta válido algo
como:
2.2 Variables
Este ejemplo hace tres asignaciones. La primera asigna una cadena a una
variable nueva llamada mensaje; la segunda asigna el entero 17 a n; la
tercera asigna el valor (aproximado) de π a pi.
>>> print n
17
>>> print pi
3.14159265359
>>> type(mensaje)
<type 'str'>
>>> type(n)
<type 'int'>
>>> type(pi)
<type 'float'>
Pues resulta que class es una de las palabras clave de Python. El intérprete
usa palabras clave para reconocer la estructura del programa, y esas
palabras no pueden ser utilizadas como nombres de variables.
2.4 Sentencias
print 1
x = 2
print x
produce la salida
1
2
>>> minuto = 59
>>> minuto/60
0
>>> minuto/60.0
0.98333333333333328
2.6 Expresiones
>>> 1 + 1
2
Sin embargo, en un script, ¡una expresión por si misma no hace nada! Esto a
menudo puede producir confusión entre los principiantes.
5
x = 5
x + 1
>>> cociente = 7 / 3
>>> print cociente
2
>>> resto = 7 % 3
>>> print resto
1
>>> primero = 10
>>> segundo = 15
>>> print primero+segundo
25
>>> primero = '100'
>>> segundo = '150'
>>> print primero + segundo
100150
A veces puede que queramos recibir el valor de una variable del usuario, a
través del teclado. Python proporciona una función integrada llamada
raw_input que recibe la entrada desde el teclado5. Cuando se llama a esa
función, el programa se detiene y espera a que el usuario escriba algo.
Cuando el usuario pulsa Retorno o Intro, el programa continúa y
raw_input devuelve como una cadena aquello que el usuario escribió.
2.11 Comentarios
A medida que los programas se van volviendo más grandes y complicados,
se vuelven más difíciles de leer. Los lenguajes formales son densos, y a
menudo es complicado mirar un trozo de código e imaginarse qué es lo que
hace, o por qué.
Por eso es buena idea añadir notas a tus programas, para explicar en
lenguaje natural qué es lo que el programa está haciendo. Estas notas reciben
el nombre de comentarios, y en Python comienzan con el símbolo #:
En este caso, el comentario aparece como una línea completa. Pero también
puedes poner comentarios al final de una línea
v = 5 # asigna 5 a v
v = 5 # velocidad en metros/segundo.
a = 35.0
b = 12.50
c = a * b
print c
horas = 35.0
tarifa = 12.50
salario = horas * tarifa
print salario
x1q3z9ahd = 35.0
x1q3z9afd = 12.50
x1q3p9afd = x1q3z9ahd * x1q3z9afd
print x1q3p9afd
A pesar de que todo esto parezca estupendo, y de que sea una idea muy
buena usar nombres de variables mnemónicos, ese tipo de nombres pueden
interponerse en el camino de los programadores novatos a la hora de
analizar y comprender el código. Esto se debe a que los programadores
principiantes no han memorizado aún las palabras reservadas (sólo hay 31),
y a veces variables con nombres que son demasiado descriptivos pueden
llegar a parecerles parte del lenguaje y no simplemente nombres de variable
bien elegidos7.
¿Qué ocurre aquí? ¿Cuáles de las piezas (for, word, in, etc.) son palabras
reservadas y cuáles son simplemente nombres de variables? ¿Acaso Python
comprende de un modo básico la noción de palabras (words)? Los
programadores novatos tienen problemas separando qué parte del código
debe mantenerse tal como está en este ejemplo y qué partes son simplemente
elección del programador.
Para los principiantes es más fácil estudiar este código y saber qué partes
son palabras reservadas definidas por Python y qué partes son simplemente
nombres de variables elegidas por el programador. Está bastante claro que
Python no entiende nada de pizza ni de porciones, ni del hecho de que una
pizza consiste en un conjunto de una o más porciones.
Las partes del código que están definidas por Python (for, in, print, y :)
están en negrita, mientras que las variables elegidas por el programador
(word y words) no lo están. Muchos editores de texto son conscientes de la
sintaxis de Python y colorearán las palabras reservadas de forma diferente
para darte pistas que te permitan mantener tus variables y las palabras
reservadas separados. Dentro de poco empezarás a leer Python y podrás
determinar rápidamente qué es una variable y qué es una palabra reservada.
2.13 Depuración
En este punto, el error de sintaxis que es más probable que cometas será
intentar utilizar nombres de variables no válidos, como class y yield, que
son palabras clave, o odd~job y US$, que contienen caracteres no válidos.
Los nombres de las variables son sensibles a mayúsculas, así que LaTeX no
es lo mismo que latex.
2.14 Glosario
asignación:
Una sentencia que asigna un valor a una variable.
cadena:
Un tipo que representa secuencias de caracteres.
concatenar:
Unir dos operandos, uno a continuación del otro.
comentario:
Información en un programa que se pone para otros programadores (o
para cualquiera que lea el código fuente), y no tiene efecto alguno en la
ejecución del programa.
división entera:
La operación que divide dos números y trunca la parte fraccionaria.
entero:
Un tipo que representa números enteros.
evaluar:
Simplificar una expresión realizando las operaciones en orden para
obtener un único valor.
expresión:
Una combinación de variables, operadores y valores que representan un
único valor resultante.
mnemónico:
Una ayuda para memorizar. A menudo damos nombres mnemónicos a las
variables para ayudarnos a recordar qué está almacenado en ellas.
palabra clave:
Una palabra reservada que es usada por el compilador para analizar un
programa; no se pueden usar palabres clave como if, def, y while
como nombres de variables.
punto flotante:
Un tipo que representa números con parte decimal.
operador:
Un símbolo especial que representa un cálculo simple, como suma,
multiplicación o concatenación de cadenas.
operador módulo:
Un operador, representado por un signo de porcentaje (%), que funciona
con enteros y obtiene el resto cuando un número es dividido por otro.
operando:
Uno de los valores con los cuales un operador opera.
reglas de precedencia:
El conjunto de reglas que gobierna el orden en el cual son evaluadas las
expresiones que involucran a múltiples operadores.
sentencia:
Una sección del código que representa un comando o acción. Hasta
ahora, las únicas sentencias que hemos visto son asignaciones y
sentencias print.
tipo:
Una categoría de valores. Los tipos que hemos visto hasta ahora son
enteros (tipo int), números en punto flotante (tipo float), y cadenas
(type str).
valor:
Una de las unidades básicas de datos, como un número o una cadena,
que un programa manipula.
variable:
Un nombre que hace referencia a un valor.
2.15 Ejercicios
Introduce Horas: 35
Introduce Tarifa: 2.75
Salario: 96.25
Por ahora no es necesario preocuparse de que nuestro salario tenga
exactamente dos dígitos después del punto decimal. Si quieres, puedes
probar la función interna de Python round para redondear de forma
adecuada el salario resultante a dos dígitos decimales.
ancho = 17
alto = 12.0
1. ancho/2
2. ancho/2.0
3. alto/3
4. 1 + 2 * 5
1
En el mundo anglosajón (y también en Python) la parte decimal de un
número se separa de la parte entera mediante un punto, y no mediante
una coma (Nota del trad.)
2
En el mundo anglosajón el “separador de millares” es la coma, y no el
punto (Nota del trad.)
3
En Python 3.0, exec ya no es una palabra clave, pero nonlocal sí que
lo es.
4
En Python 3.0, el resultado de esta división es un número flotante. En
Python 3.0, el nuevo operador // es el que realiza la división entera.
5
En Python 3.0, esta función ha sido llamada input.
6
Consulta https://es.wikipedia.org/wiki/Mnemonico para obtener
una descripción detallada de la palabra “mnemónico”.
7
El párrafo anterior se refiere más bien a quienes eligen nombres de
variables en inglés, ya que todas las palabras reservadas de Python
coinciden con palabras propias de ese idioma (Nota del trad.)
Chapítulo 3 Ejecución condicional
Una expresión booleana es aquella que puede ser verdadera (True) o falsa
(False). Los ejemplos siguientes usan el operador ==, que compara dos
operandos y devuelve True si son iguales y False en caso contrario:
>>> 5 == 5
True
>>> 5 == 6
False
>>> type(True)
<type 'bool'>
>>> type(False)
<type 'bool'>
x != y # x es distinto de y
x > y # x es mayor que y
x < y # x es menor que y
x >= y # x es mayor o igual que y
x <= y # x es menor o igual que y
x is y # x es lo mismo que y
x is not y # x no es lo mismo que y
Finalmente, el operador not niega una expresión booleana, de modo que not
(x > y) es verdadero si x > y es falso; es decir, si x es menor o igual que
y.
Esta flexibilidad puede ser útil, pero existen ciertas sutilezas en este tipo de
uso que pueden resultar confusas. Es posible que prefieras evitar usarlo de
este modo hasta que estés bien seguro de lo que estás haciendo.
if x > 0 :
print 'x es positivo'
if x < 0 :
pass # ¡necesito controlar los valores
negativos!
>>> x = 3
>>> if x < 10:
... print 'Pequeño'
...
Pequeño
>>>
if x%2 == 0 :
print 'x es par'
else :
print 'x es impar'
Algunas veces hay más de dos posibilidades, de modo que necesitamos más
de dos ramas. Una forma de expresar una operación como ésa es usar un
condicional encadenado:
if x < y:
print 'x es menor que y'
elif x > y:
print 'x es mayor que y'
else:
print 'x e y son iguales'
elif es una abreviatura para “else if”. En este caso también será ejecutada
únicamente una de las ramas.
No hay un límite para el número de sentencias elif. Si hay una clausula
else, debe ir al final, pero tampoco es obligatorio que ésta exista.
if choice == 'a':
print 'Respuesta incorrecta'
elif choice == 'b':
print 'Respuesta correcta'
elif choice == 'c':
print 'Casi, pero no es correcto'
if x == y:
print 'x e y son iguales'
else:
if x < y:
print 'x es menor que y'
else:
print 'x es mayor que y'
if 0 < x:
if x < 10:
print 'x es un número positivo con un sólo
dígito.'
python fahren.py
Introduce la Temperatura Fahrenheit:72
22.2222222222
python fahren.py
Introduce la Temperatura Fahrenheit:fred
Traceback (most recent call last):
File "fahren.py", line 2, in <module>
fahr = float(ent)
ValueError: invalid literal for float(): fred
python fahren2.py
Introduce la Temperatura Fahrenheit:72
22.2222222222
python fahren2.py
Introduce la Temperatura Fahrenheit:fred
Por favor, introduce un número
Manejar una excepción con una sentencia try recibe el nombre de capturar
una excepción. En este ejemplo, la clausula except muestra un mensaje de
error. En general, capturar una excepción te da la oportunidad de corregir el
problema, volverlo a intentar, o al menos de terminar el programa con
elegancia.
Cuando Python está procesando una expresión lógica, como x >= 2 and
(x/y) > 2, evalúa la expresión de izquierda a derecha. Debido a la
definición de and, si x es menor de 2, la expresión x >= 2 resulta ser
falsa, de modo que la expresión completa ya va a resultar falsa,
independientemente de si (x/y) > 2 se evalúa como verdadero o falso.
>>> x = 6
>>> y = 2
>>> x >= 2 and (x/y) > 2
True
>>> x = 1
>>> y = 0
>>> x >= 2 and (x/y) > 2
False
>>> x = 6
>>> y = 0
>>> x >= 2 and (x/y) > 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
>>>
>>> x = 1
>>> y = 0
>>> x >= 2 and y != 0 and (x/y) > 2
False
>>> x = 6
>>> y = 0
>>> x >= 2 and y != 0 and (x/y) > 2
False
>>> x >= 2 and (x/y) > 2 and y != 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
>>>
>>> x = 5
>>> y = 6
File "<stdin>", line 1
y = 6
^
SyntaxError: invalid syntax
import math
int_senal = 9
int_ruido = 10
relacion = int_senal / int_ruido
decibelios = 10 * math.log10(relacion)
print decibelios
3.10 Glosario
condición:
La expresión booleana en una sentencia condicional que determina qué
rama será ejecutada.
condicional anidado:
Una sentencia condicional que aparece en una de las ramas de otra
sentencia condicional.
condicional encadenado:
Una sentencia condicional con una serie de ramas alternativas.
cortocircuito:
Cuando Python va evaluando una expresión lógica por tramos y detiene
el proceso de evaluación debido a que ya conoce el valor final que va a
tener el resultado sin necesidad de evaluar el resto de la expresión.
cuerpo:
La secuencia de sentencias en el interior de una sentencia compuesta.
expresión booleana:
Un expresión cuyo valor puede ser o bien Verdadero o bien Falso.
operadores de comparación:
Uno de los operadores que se utiliza para comparar dos operandos: ==,
!=, >, <, >=, y <=.
operador lógico:
Uno de los operadores que se combinan en las expresiones booleanas:
and, or, y not.
patrón guardián:
Cuando construimos una expresión lógica con comparaciones
adicionales para aprovecharnos del funcionamiento en cortocircuito.
rama:
Una de las secuencias alternativas de sentencias en una sentencia
condicional.
sentencia compuesta:
Una sentencia que consiste en un encabezado y un cuerpo. El
encabezado termina con dos-puntos (:). El cuerpo está sangrado con
relación al encabezado.
sentencia condicional:
Una sentencia que controla el flujo de ejecución, dependiendo de cierta
condición.
traceback:
Una lista de las funciones que se están ejecutando, que se muestra en
pantalla cuando se produce una excepción.
3.11 Ejercicios
Puntuación Calificación
>= 0.9 Sobresaliente
>= 0.8 Notable
>= 0.7 Bien
>= 0.6 Suficiente
< 0.6 Insuficiente
1
Estudiaremos las funciones en el capítulo 4 y los bucles en el capítulo
5.
2
En Python 3.0, ya no se produce el mensaje de error; el operador de
división realiza división en punto flotante incluso con operandos
enteros.
Chapítulo 4 Funciones
>>> type(32)
<type 'int'>
Las funciones max y min nos darán respectivamente el valor mayor y menor
de una lista:
La función max nos dice cuál es el “carácter más grande” de la cadena (que
resulta ser la letra “u”), mientras que la función min nos muestra el carácter
más pequeño (que en ese caso es un espacio).
Otra función incorporada muy común es len, que nos dice cuántos elementos
hay en su argumento. Si el argumento de len es una cadena, nos devuelve el
número de caracteres que hay en la cadena.
>>> int('32')
32
>>> int('Hola')
ValueError: invalid literal for int(): Hola
>>> float(32)
32.0
>>> float('3.14159')
3.14159
>>> str(32)
'32'
>>> str(3.14159)
'3.14159'
A partir de las mismas entradas, la mayoría de los PCs generarán las mismas
salidas cada vez, que es lo que llamamos comportamiento determinista. El
determinismo normalmente es algo bueno, ya que esperamos que la misma
operación nos proporcione siempre el mismo resultado. Para ciertas
aplicaciones, sin embargo, querremos que el equipo sea impredecible. Los
juegos son el ejemplo obvio, pero hay más.
import random
for i in range(10):
x = random.random()
print x
0.301927091705
0.513787075867
0.319470430881
0.285145917252
0.839069045123
0.322027080731
0.550722110248
0.366591677812
0.396981483964
0.838116437404
La función random es solamente una de las muchas que trabajan con números
aleatorios. La función randint toma los parámetros inferior y superior,
y devuelve un entero entre inferior y superior (incluyendo ambos
extremos).
>>> random.randint(5, 10)
5
>>> random.randint(5, 10)
9
>>> t = [1, 2, 3]
>>> random.choice(t)
2
>>> random.choice(t)
3
>>> grados = 45
>>> radianes = grados / 360.0 * 2 * math.pi
>>> math.sin(radianes)
0.707106781187
He aquí un ejemplo:
def muestra_estribillo():
print "Soy un leñador, qué alegría."
print 'Duermo toda la noche y trabajo todo el día.'
def es una palabra clave que indica que se trata de una definición de
función. El nombre de la función es muestra_estribillo. Las reglas para
los nombres de las funciones son los mismos que para las variables: se
pueden usar letras, números y algunos signos de puntuación, pero el primer
carácter no puede ser un número. No se puede usar una palabra clave como
nombre de una función, y se debería evitar también tener una variable y una
función con el mismo nombre.
Los paréntesis vacíos después del nombre indican que esta función no toma
ningún argumento. Más tarde construiremos funciones que toman argumentos
de entrada.
>>> muestra_estribillo()
Soy un leñador, qué alegría.
Duermo toda la noche y trabajo todo el día.
Una vez que se ha definido una función, puede usarse dentro de otra. Por
ejemplo, para repetir el estribillo anterior, podríamos escribir una función
llamada repite_estribillo:
def repite_estribillo():
muestra_estribillo()
muestra_estribillo()
>>> repite_estribillo()
Soy un leñador, qué alegría.
Duermo toda la noche y trabajo todo el día.
Soy un leñador, qué alegría.
Duermo toda la noche y trabajo todo el día.
def muestra_estribillo():
print "Soy un leñador, que alegría."
print 'Duermo toda la noche y trabajo todo el día.'
def repite_estribillo():
muestra_estribillo()
muestra_estribillo()
repite_estribillo()
Para asegurarnos de que una función está definida antes de usarla por
primera vez, es necesario saber el orden en que las sentencias son
ejecutadas, que es lo que llamamos el flujo de ejecución.
Esto parece bastante simple, hasta que uno recuerda que una función puede
llamar a otra. Cuando está en medio de una función, el programa puede tener
que ejecutar las sentencias de otra función. Pero cuando está ejecutando esa
nueva función, ¡tal vez haya que ejecutar más funciones todavía!
Algunas de las funciones internas que hemos visto necesitan argumentos. Por
ejemplo, cuando se llama a math.sin, se le pasa un número como
argumento. Algunas funciones necesitan más de un argumento: math.pow
toma dos, la base y el exponente.
def muestra_dos_veces(bruce):
print bruce
print bruce
Esta función funciona con cualquier valor que pueda ser mostrado en
pantalla.
>>> muestra_dos_veces('Spam')
Spam
Spam
>>> muestra_dos_veces(17)
17
17
>>> muestra_dos_veces(math.pi)
3.14159265359
3.14159265359
El argumento es evaluado antes de que función sea llamada, así que en los
ejemplos, la expresión 'Spam '*4 y math.cos(math.pi) son evaluadas
sólo una vez.
Cuando llamas a una función productiva, casi siempre querrás hacer luego
algo con el resultado; por ejemplo, puede que quieras asignarlo a una
variable o usarlo como parte de una expresión:
x = math.cos(radians)
aurea = (math.sqrt(5) + 1) / 2
>>> math.sqrt(5)
2.2360679774997898
math.sqrt(5)
x = sumados(3, 5)
print x
Puede no estar muy claro por qué merece la pena molestarse en dividir un
programa en funciones. Existen varias razones:
A lo largo del resto del libro, a menudo usaremos una definición de función
para explicar un concepto. Parte de la habilidad de crear y usar funciones
consiste en llegar a tener una función que capture correctamente una idea,
como “encontrar el valor más pequeño en una lista de valores”. Más
adelante te mostraremos el código para encontrar el valor más pequeño de
una lista de valores y te lo presentaremos como una función llamada min,
que toma una lista de valores como argumento y devuelve el menor valor de
esa lista.
4.12 Depuración
Si estás usando un editor de texto para escribir tus propios scripts, puede
que tengas problemas con los espacios y tabulaciones. El mejor modo de
evitar esos problemas es usar espacios exclusivamente (no tabulaciones). La
mayoría de los editores de texto que reconocen Python lo hacen así por
efecto, aunque hay algunos que no.
Las tabulaciones y los espacios normalmente son invisibles, lo cual hace que
sea difícil depurar los errores que se pueden producir, así que mejor busca
un editor que gestione el sangrado por ti.
4.13 Glosario
algoritmo:
Un proceso general para resolver una categoría de problemas.
argumento:
Un valor proporcionado a una función cuando ésta es llamada. Ese valor
se asigna al parámetro correspondiente en la función.
cabecera:
La primera línea de una definición de función.
cuerpo:
La secuencia de sentencias dentro de la definición de una función.
composición:
Uso de una expresión o sentencia como parte de otra más larga,
definición de función:
Una sentencia que crea una función nueva, especificando su nombre,
parámetros, y las sentencias que ejecuta.
determinístico:
Perteneciente a un programa que hace lo mismo cada vez que se ejecuta,
a partir de las mismas entradas.
función:
Una secuencia de sentencias con un nombre que realizan alguna
operación útil. Las funciones pueden tomar argumentos o no, y pueden
producir un resultado o no.
función productiva (fruitful function):
Una función que devuelve un valor.
función estéril (void function):
Una función que no devuelve ningún valor.
flujo de ejecución:
El orden en el cual se ejecutan las sentencias durante el funcionamiento
de un programa.
llamada a función:
Una sentencia que ejecuta una función. Consiste en el nombre de la
función seguido por una lista de argumentos.
notación punto:
La sintaxis de llamada a una función en otro módulo, especificando el
nombre del módulo seguido por un punto y el nombre de la función.
objeto función:
Un valor creado por una definición de función. El nombre de la función
es una variable que se refiere al objeto función.
objeto módulo:
Un valor creado por una sentencia import, que proporciona acceso a
los datos y código definidos en un módulo.
parámetro:
Un nombre usado dentro de una función para referirse al valor pasado
como argumento.
pseudoaleatorio:
Perteneciente a una secuencia de números que parecen ser aleatorios,
pero son generados por un programa determinista.
sentencia import:
Una sentencia que lee un archivo módulo y crea un objeto módulo.
valor de retorno:
El resultado de una función. Si una llamada a una función es usada como
una expresión, el valor de retorno es el valor de la expresión.
4.14 Ejercicios
def fred():
print "Zap"
def jane():
print "ABC"
jane()
fred()
jane()
Introduce Horas: 45
Introduce Tarifa: 10
Salario: 475.0
Puntuación Calificación
> 0.9 Sobresaliente
> 0.8 Notable
> 0.7 Bien
> 0.6 Suficiente
<= 0.6 Insuficiente
x = x+1
>>> x = x+1
NameError: name 'x' is not defined
>>> x = 0
>>> x = x+1
n = 5
while n > 0:
print n
n = n-1
print '¡Despegue!'
Este tipo de flujo recibe el nombre de bucle, ya que el tercer paso enlaza de
nuevo con el primero. Cada vez que se ejecuta el cuerpo del bucle se dice
que realizamos una iteración. Para el bucle anterior, podríamos decir que
“ha tenido cinco iteraciones”, lo que significa que el cuerpo del bucle se ha
ejecutado cinco veces.
El cuerpo del bucle debe cambiar el valor de una o más variables, de modo
que la condición pueda en algún momento evaluarse como falsa y el bucle
termine. La variable que cambia cada vez que el bucle se ejecuta y controla
cuándo termina éste, recibe el nombre de variable de iteración. Si no hay
variable de iteración, el bucle se repetirá para siempre, resultando así un
bucle infinito.
n = 10
while True:
print n,
n = n - 1
print '¡Terminado!'
A pesar de que en este caso se trata de un bucle infinito inútil, se puede usar
ese diseño para construir bucles útiles, siempre que se tenga la precaución
de añadir código en el cuerpo del bucle para salir explícitamente, usando
break cuando se haya alcanzado la condición de salida.
Por ejemplo, supón que quieres recoger entradas de texto del usuario hasta
que éste escriba fin. Podrías escribir:
while True:
linea = raw_input('> ')
if linea == 'fin':
break
print linea
print '¡Terminado!'
while True:
linea = raw_input('> ')
if linea[0] == '#' :
continue
if linea == 'fin':
break
print linea
print '¡Terminado!'
A menudo se usa un bucle for o while para movernos a través de una lista
de elementos o el contenido de un archivo y se busca algo, como el valor
más grande o el más pequeño de los datos que estamos revisando.
total = 0
for valor in [3, 41, 12, 9, 74, 15]:
total = total + valor
print 'Total: ', total
mayor = None
print 'Antes:', mayor
for valor in [3, 41, 12, 9, 74, 15]:
if mayor is None or valor > mayor :
mayor = valor
print 'Bucle:', valor, mayor
print 'Mayor:', mayor
Antes: None
Bucle: 3 3
Bucle: 41 41
Bucle: 12 41
Bucle: 9 41
Bucle: 74 74
Bucle: 15 74
Mayor: 74
Debemos pensar en la variable mayor como el “mayor valor visto hasta ese
momento”. Antes del bucle, asignamos a mayor el valor None. None es un
valor constante especial que se puede almacenar en una variable para
indicar que la variable está “vacía”.
Antes de que el bucle comience, el mayor valor visto hasta entonces es None,
dado que no se ha visto aún ningún valor. Durante la ejecución del bucle, si
mayor es None, entonces tomamos el primer valor que hemos visto como el
mayor hasta entonces. Se puede ver en la primera iteración, cuando el valor
de valor es 3, mientras que mayor es None, inmediatamente mayor pasa a
ser 3.
Al final del bucle, se habrán revisado todos los valores y la variable mayor
contendrá entonces el mayor valor de la lista.
menor = None
print 'Antes:', menor
for valor in [3, 41, 12, 9, 74, 15]:
if menor is None or valor < menor:
menor = valor
print 'Bucle:', valor, menor
print 'Menor:', menor
def min(valores):
menor = None
for valor in valores:
if menor is None or valor < menor:
menor = valor
return menor
5.8 Depuración
A medida que vayas escribiendo programas más grandes, puede que notes
que vas necesitando emplear cada vez más tiempo en depurarlos. Más
código significa más oportunidades de cometer un error y más lugares para
que los bugs puedan esconderse.
En lugar de eso, intenta partir el problema por la mitad. Busca en medio del
programa, o cerca de ahí, un valor intermedio que puedas comprobar. Añade
una sentencia print (o alguna otra cosa que tenga un efecto verificable), y
haz funcionar el programa.
Cada vez que realices una comprobación como esta, reduces a la mitad el
número de líneas en las que buscar. Después de seis pasos (que son muchos
menos de 100), lo habrás reducido a una o dos líneas de código, al menos en
teoría.
5.9 Glosario
acumulador:
Una variable usada en un bucle para sumar o acumular un resultado.
bucle infinito:
Un bucle en el cual la condición de terminación no se satisface nunca o
para el cual no existe dicha condición de terminación.
contador:
Una variable usada en un bucle para contar el número de veces que algo
sucede. Inicializamos el contador a cero y luego lo vamos
incrementando cada vez que queramos que “cuente” algo.
decremento:
Una actualización que disminuye el valor de una variable.
inicializar:
Una asignación que da un valor inicial a una variable que va a ser
después actualizada.
incremento:
Una actualización que aumenta el valor de una variable (a menudo en
una unidad).
iteración:
Ejecución repetida de una serie de sentencias usando bien una función
que se llama a si misma o bien un bucle.
5.10 Ejercicios
Introduce un número: 4
Introduce un número: 5
Introduce un número: dato erróneo
Entrada inválida
Introduce un número: 7
Introduce un número: fin
16 3 5.33333333333
Ejercicio 2 Escribe otro programa que pida una lista de números como la
anterior y al final muestre por pantalla el máximo y mínimo de los
números, en vez de la media.
1
Examinaremos las listas con más detalle en un capítulo posterior.
Chapítulo 6 Cadenas
Para obtener la última letra de una cadena, puedes sentirte tentado a intentar
algo como esto:
Gran parte de las operaciones implican procesar una cadena carácter por
carácter. A menudo se empieza por el principio, se van seleccionando
caracteres de uno en uno, se hace algo con ellos, y se continúa hasta el final.
Este modelo de procesado recibe el nombre de recorrido. Una forma de
escribir un recorrido es usar un bucle while:
indice = 0
while indice < len(fruta):
letra = fruta[indice]
print letra
indice = indice + 1
Una cadena vacía no contiene caracteres y tiene una longitud 0, pero por lo
demás es exactamente igual que cualquier otra cadena.
La razón del error es que las cadenas son inmutables, lo cual significa que
no se puede cambiar una cadena existente. Lo mejor que se puede hacer en
estos casos es crear una cadena nueva que sea una variación de la original:
Este ejemplo concatena una primera letra nueva en una rebanada de saludo.
Esto conserva intacta la cadena original.
palabra = 'banana'
contador = 0
for letra in palabra:
if letra == 'a':
contador = contador + 1
print contador
Ejercicio 3
6.7 El operador in
if palabra == 'banana':
print 'De acuerdo, bananas.'
Python dispone de una función llamada dir que lista los métodos
disponibles para un objeto. La función type muestra el tipo de cualquier
objeto y la función dir muestra los métodos disponibles.
capitalize(...)
S.capitalize() -> string
A pesar de que la función dir lista los métodos, y de que puedes usar help
para obtener un poco de información sobre cada método, una fuente de
documentación mejor para los métodos de las cadenas se puede encontrar en
https://docs.python.org/2/library/stdtypes.html#string-
methods.
Por ejemplo, el método upper toma una cadena y devuelve otra nueva con
todas las letras en mayúsculas:
Esta forma de notación con punto especifica el nombre del método, upper, y
el nombre de la cadena a la cual se debe aplicar ese método, palabra. Los
paréntesis vacíos indican que el método no toma argumentos.
>>> palabra.find('na')
2
>>> palabra.find('na', 3)
4
Una tarea habitual es eliminar espacios en blanco (espacios, tabulaciones,
saltos de línea) del comienzo y del final de una cadena usando el método
strip:
Ejercicio 4
Existe un método de cadena llamado count, que es similar a la función
que vimos en el ejercicio anterior. Lee la documentación de este método en
https://docs.python.org/2/library/stdtypes.html#string-
methods y escribe una invocación que cuente el número de veces que
aparece la letra “a” en 'banana'.
A menudo tendremos que mirar en el interior una cadena para localizar una
subcadena. Por ejemplo, si se nos presentan una serie de líneas formateadas
de este modo:
>>> camellos = 42
>>> '%d' % camellos
'42'
El resultado es la cadena '42', que no hay que confundir con el valor entero
42.
>>> camellos = 42
>>> 'He visto %d camellos.' % camellos
'He visto 42 camellos.'
Si hay más de una secuencia de formato en la cadena, el segundo argumento
debe ser una tupla1. Cada secuencia de formato se corresponde con un
elemento de la tupla, en orden.
El ejemplo siguiente usa '%d' para formatear un entero, '%g' para formatear
un número en punto flotante (no preguntes por qué), y '%s' para formatear
una cadena:
6.12 Depuración
while True:
linea = raw_input('> ')
if linea[0] == '#' :
continue
if linea == 'fin':
break
print linea
print '¡Terminado!'
Mira lo que sucede cuando el usuario introduce una línea vacía como
entrada:
El código funciona hasta que se le presenta una línea vacía. Entonces, como
no hay carácter cero-ésimo, obtenemos un traceback. Existen dos soluciones
a esto para convertir la línea tres en “segura”, incluso cuando la entrada sea
una cadena vacía.
if linea.startswith('#') :
Otro modo es asegurar la sentencia if usando el patrón guardián, y
asegurarnos de que la segunda expresión lógica sea evaluada sólo cuando
hay al menos un carácter en la cadena:
6.13 Glosario
búsqueda:
Un patrón de recorrido que se detiene cuando encuentra lo que está
buscando.
cadena vacía:
Una cadena sin caracteres y de longitud 0, representada por dos
comillas.
contador:
Una variable utilizada para contar algo, normalmente inicializada a cero
y luego incrementada.
cadena a formatear:
Una cadena, usada con el operador de formato, que contiene secuencias
de formato.
elemento:
Uno de los valores en una secuencia.
flag (bandera):
Una variable booleana que se usa para indicar si una condición es
verdadera.
índice:
Un valor entero usado para seleccionar un elemento en una secuencia,
como un carácter en una cadena.
inmutable:
La propiedad de una secuencia cuyos elementos no pueden ser
asignados.
invocación:
Una sentencia que llama a un método.
método:
Una función que está asociada con un objeto y es llamada usando la
notación punto.
objecto:
Algo a lo que puede referirse una variable. Por ahora, puedes usar
“objeto” y “valor” indistintamente.
operador de formato:
Un operador, %, que toma una cadena a formatear y una tupla y genera
una cadena que incluye los elementos de la tupla formateados como se
especifica en la cadena de formato.
rebanada (slice):
Una parte de una cadena especificada por un rango de índices.
recorrido:
Iterar a través de los elementos de una secuencia, realizando un
operación similar en cada uno de ellos.
secuencia:
Un conjunto ordenado; es decir, un conjunto de valores donde cada
valor está identificado por un índice entero.
secuencia de formato:
Una secuencia de caracteres en una cadena de formato, como %d, que
especifica cómo debe ser formateado un valor.
6.14 Ejercicios
1
Una tupla es una secuencia de valores separados por comas dentro de
unos paréntesis. Veremos las tuplas en el capítulo 10
Chapítulo 7 Ficheros
7.1 Persistencia
Más adelante usaremos try y except para controlar con más elegancia la
situación cuando intentemos abrir un archivo que no existe.
Así que el carácter de salto de línea separa los caracteres del fichero en
líneas.
7.4 Lectura de ficheros
manf = open('mbox.txt')
contador = 0
for linea in manf:
contador = contador + 1
print 'Líneas contabilizadas:', contador
python open.py
Line Count: 132045
Podemos usar el manejador del fichero como una secuencia en nuestro bucle
for. El bucle for simplemente cuenta el número de líneas del fichero y lo
muestra en pantalla. La traducción aproximada del bucle for al español es:
“para cada línea del fichero representado por este manejador, añade una
unidad a la variable contador”.
Debido a que el bucle for lee los datos línea a línea, puede leer y contar las
líneas de forma eficiente en ficheros muy extensos sin agotar la memoria de
almacenamiento de datos. El programa anterior puede contar las líneas de
ficheros de cualquier tamaño usando muy poca memoria, ya que cada línea
es leída, contada y luego descartada.
Cuando el archivo se lee de esta manera, todos los caracteres incluyendo las
líneas completas y los saltos de línea forman parte de una gran cadena que se
guarda en la variable ent. Recuerda que esta forma de uso de la función
open sólo debe utilizarse si el fichero de datos cabe holgadamente en la
memoria principal de tu equipo.
manf = open('mbox-short.txt')
for linea in manf:
if linea.startswith('From:') :
print linea
From: [email protected]
From: [email protected]
From: [email protected]
From: [email protected]
...
La salida parece ser correcta, ya que las únicas líneas que vemos son
aquellas que comienzan por “From:”. Pero, ¿por qué estamos viendo líneas
extra vacías? Esto se debe al carácter invisible de salto de línea. Cada una
de las líneas termina con un salto de línea, de modo que la sentencia print
imprime la cadena que está en la variable linea y que incluye un salto de
línea, y a continuación print añade otro salto de línea, cuyo resultado es el
efecto de doble espaciado que podemos ver.
manf = open('mbox-short.txt')
for linea in manf:
linea = linea.rstrip()
if linea.startswith('From:') :
print linea
From: [email protected]
From: [email protected]
From: [email protected]
From: [email protected]
From: [email protected]
From: [email protected]
From: [email protected]
...
Podemos estructurar el bucle para usar ese diseño y saltar las líneas que no
nos interesan, de este modo:
manf = open('mbox-short.txt')
for linea in manf:
linea = linea.rstrip()
# Saltar 'líneas que no nos interesan'
if not linea.startswith('From:') :
continue
# Procesar nuestra línea 'interesante'
print linea
manf = open('mbox-short.txt')
for linea in manf:
linea = linea.rstrip()
if linea.find('@uct.ac.za') == -1 :
continue
print linea
Lo más probable es que no nos apetezca editar nuestro código Python cada
vez que queramos procesar un archivo diferente. Sería más útil pedir al
usuario que introdujera una cadena con el nombre del fichero cada vez que el
programa funcione, de modo que pueda usar nuestro programa sobre ficheros
diferentes sin tener que cambiar el código Python.
python search6.py
Introduce el nombre del fichero: mbox.txt
Hay 1797 líneas subject en mbox.txt
python search6.py
Introduce el nombre del fichero: mbox-short.txt
Hay 27 líneas subject en mbox-short.txt
python search6.py
Introduce el nombre del fichero: perdido.txt
Traceback (most recent call last):
File "search6.py", line 2, in <module>
manf = open(nombref)
IOError: [Errno 2] No such file or directory:
'perdido.txt'
python search6.py
Introduce el nombre del fichero: na na boo boo
Traceback (most recent call last):
File "search6.py", line 2, in <module>
manf = open(nombref)
IOError: [Errno 2] No such file or directory: 'na na
boo boo'
No te rías, los usuarios harán de vez en cuando todo lo que puedan por
estropear tus programas—ya sea a propósito o con malas intenciones. De
hecho, una parte importante de cualquier equipo de desarrollo de software es
una persona o grupo llamado Controlador de Calidad (o QA por sus siglas
en inglés), cuyo trabajo principal es hacer las cosas más disparatadas
posibles para intentar hacer fallar el software que el programador ha creado.
contador = 0
for linea in manf:
if linea.startswith('Subject:') :
contador = contador + 1
print 'Hay', contador, 'líneas subject en', nombref
python search7.py
Introduce el nombre del fichero: mbox.txt
Hay 1797 líneas subject en mbox.txt
python search7.py
Introduce el nombre del fichero: na na boo boo
No se pudo abrir el fichero: na na boo boo
Una vez que adquieras más soltura en Python, puedes intercambiar opiniones
con otros programadores de Python para decidir cual de dos soluciones
equivalente para un problema es “más Pythónica”. La ambición de ser “más
Pythónico” capta la idea de que programar es parte ingeniería y parte arte.
No siempre estamos interesados únicamente en hacer que algo funcione sin
más, también queremos que nuestra solución sea elegante y que sea
apreciada por su elegancia por nuestros colegas.
Para escribir en un fichero, debes abrirlo usando el modo 'w' (de write)
como segundo parámetro:
El método write del objeto manejador del fichero pone datos dentro del
archivo.
>>> fsal.close()
También se pueden cerrar los ficheros que se han abierto en modo lectura,
pero no es necesario que seamos muy estrictos con ello si solamente estamos
abriendo unos pocos archivos, ya que Python se asegura de que todos los
ficheros queden cerrados cuando el programa termina. En cambio, en el caso
de que estemos escribiendo ficheros, deberemos cerrarlos explícitamente
para no dejar nada al azar.
7.9 Depuración
7.10 Glosario
capturar (catch):
Evitar que una excepción haga terminar un programa, usando las
sentencias try y except.
Controlador de Calidad:
Una persona o equipo centrada en asegurar la calidad del conjunto en un
producto software. Sus siglas en inglés son QA (Quality Assurance). El
QA se encarga normalmente de probar un producto e identificar sus
problemas antes de que éste sea lanzado.
fichero de texto:
Una secuencia de caracteres almacenados en un medio de
almacenamiento permanente, como un disco duro.
Pythónico:
Una técnica que funciona de forma elegante en Python. “Usar try y
except y es la forma Pythónica de restablecer un programa en caso de
intentar abrir archivos que no existen”.
salto de línea:
Un carácter especial que se utiliza en los archivos y cadenas para
indicar el final de una línea.
7.11 Ejercicios
Ejercicio 1 Escribe un programa que lea un fichero e imprima en
pantalla su contenido (línea a línea), todo en mayúsculas. La ejecución del
programa debería ser algo similar a esto:
python shout.py
Introduce el nombre del fichero: mbox-short.txt
FROM [email protected] SAT JAN 5 09:14:16
2008
RETURN-PATH: <[email protected]>
RECEIVED: FROM MURDER (MAIL.UMICH.EDU [141.211.14.90])
BY FRANKENSTEIN.MAIL.UMICH.EDU (CYRUS V2.3.8) WITH
LMTPA;
SAT, 05 JAN 2008 09:14:16 -0500
X-DSPAM-Confidence: 0.8475
python egg.py
Introduce el nombre del fichero: mbox.txt
Hay 1797 líneas subject en mbox.txt
python egg.py
Introduce el nombre del fichero: missing.tyxt
No se pudo abrir el fichero: missing.tyxt
python egg.py
Introduce el nombre del fichero: na na boo boo
NA NA BOO BOO PARA TI - ¡Has sido un niño malo!
Al igual que una cadena, una lista es una secuencia de valores. En una
cadena, los valores son caracteres; en una lista, pueden ser de cualquier tipo.
Los valores en las listas reciben el nombre de elementos, o a veces
artículos.
Hay varios modos de crear una lista nueva; el más simple consiste en
encerrar los elementos entre corchetes ([ y ]):
La sintaxis para acceder a los elementos de una lista es la misma que para
acceder a los caracteres de una cadena—el operador corchete. La expresión
dentro de los corchetes especifica el índice. Recuerda que los índices
comienzan por 0:
A diferencia de las cadenas, las listas son mutables (pueden mutar), porque
puedes cambiar el orden de los elementos o reasignar un elemento dentro de
la lista. Cuando el operador corchete aparece en el lado izquierdo de una
asignación, éste identifica el elemento de la lista que será asignado.
El elemento cuyo índice es uno de numeros, que antes era 123, es ahora 5.
Puedes pensar en una lista como una relación entre índices y elementos. Esta
relación recibe el nombre de mapeo o direccionamiento; cada índice
“dirige a” uno de los elementos.
Los índices de una lista funcionan del mismo modo que los índices de una
cadena:
El modo más habitual de recorrer los elementos de una lista es con un bucle
for. La sintaxis es la misma que para las cadenas:
for i in range(len(numeros)):
numeros[i] = numeros[i] * 2
Un bucle for aplicado a una lista vacía no ejecuta nunca el código contenido
en su cuerpo:
for x in vacia:
print 'Esto nunca ocurrirá.'
A pesar de que una lista puede contener otra, la lista anidada sólo cuenta
como un único elemento. La longitud de esta lista es cuatro:
>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> c = a + b
>>> print c
[1, 2, 3, 4, 5, 6]
>>> [0] * 4
[0, 0, 0, 0]
>>> [1, 2, 3] * 3
[1, 2, 3, 1, 2, 3, 1, 2, 3]
El primer ejemplo repite [0] cuatro veces. El segundo, repite la lista [1,
2, 3] tres veces.
>>> t[:]
['a', 'b', 'c', 'd', 'e', 'f']
Como las litas son mutables, a menudo resultará útil hacer una copia antes de
realizar operaciones que dupliquen elementos, los hagan rotar o mutilen de
algún modo esas listas.
Python proporciona varios métodos que operan con listas. Por ejemplo,
append añade un nuevo elemento al final de una lista:
extend toma una lista como argumento y añade al final de la actual todos sus
elementos
Hay varias formas de borrar elementos de una lista. Si conoces el índice del
elemento que quieres eliminar, puedes usar pop:
Hay varias funciones internas que pueden utilizarse en las listas y que nos
permiten buscar rápidamente a través de ellas sin tener que escribir nuestros
propios bucles:
>>> nums = [3, 41, 12, 9, 74, 15]
>>> print len(nums)
6
>>> print max(nums)
74
>>> print min(nums)
3
>>> print sum(nums)
154
>>> print sum(nums)/len(nums)
25
total = 0
contador = 0
while ( True ) :
ent = raw_input('Introduce un número: ')
if ent == 'fin' : break
valor = float(ent)
total = total + valor
contador = contador + 1
listnum = list()
while ( True ) :
ent = raw_input('Introduce un número: ')
if ent == 'fin' : break
valor = float(ent)
listnum.append(valor)
Creamos una lista vacía antes de que el bucle comience, y luego cada vez
que tenemos un número lo añadimos a la lista. Al final del programa,
simplemente calculamos la suma de los números de la lista y lo dividimos
por la cantidad de números, para conseguir la media.
>>> s = 'spam'
>>> t = list(s)
>>> print t
['s', 'p', 'a', 'm']
Debido a que list es el nombre de una función interna, debes evitar usarla
como nombre de variable. Yo también evito utilizar la letra l, porque se
parece mucho al número 1. Por eso utilizo t.
La función list divide una cadena en letras individuales. Si quieres dividir
una cadena en palabras, puedes usar el método split:
Una vez hayas usado split para dividir la cadena en una lista de palabras,
se puede utilizar el operador índice (corchetes) para buscar una palabra
concreta en la lista.
>>> s = 'spam-spam-spam'
>>> delimitador = '-'
>>> s.split(delimitador)
['spam', 'spam', 'spam']
El método split es muy efectivo cuando nos enfrentamos con este tipo de
problemas. Podemos escribir un pequeño programa que busque las líneas
que comiencen por “From ”, extraer las palabras de esas líneas con split, y
luego imprimir en pantalla la tercera palabra de cada una:
manf = open('mbox-short.txt')
for linea in manf:
linea = linea.rstrip()
if not linea.startswith('From ') : continue
palabras = linea.split()
print palabras[2]
Sat
Fri
Fri
Fri
...
Más adelante, aprenderemos técnicas más sofisticadas para seleccionar las
líneas con las que vamos a trabajar y veremos cómo extraer esas líneas para
encontrar el fragmento exacto de información que estamos buscando.
a = 'banana'
b = 'banana'
>>> a = 'banana'
>>> b = 'banana'
>>> a is b
True
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a is b
False
En este caso podríamos decir que las dos listas son equivalentes, porque
tienen los mismos elementos, pero no son idénticas, porque no son el mismo
objeto. Si dos objetos son idénticos, también son equivalentes, pero si son
equivalentes, no necesariamente son idénticos.
8.12 Alias
>>> a = [1, 2, 3]
>>> b = a
>>> b is a
True
Un objeto con más de una referencia tiene más de un nombre, de modo que
decimos que el objeto tiene uno o varios alias.
Si el objeto con alias es mutable, los cambios que se hagan en uno de los
alias afectarán al otro:
>>> b[0] = 17
>>> print a
[17, 2, 3]
A pesar de que este comportamiento puede resultar útil, es también propenso
a errores. En general, resulta más seguro evitar usar alias cuando se está
trabajando con objetos mutables.
a = 'banana'
b = 'banana'
Cuando se pasa una lista a una función, la función recibe una referencia de
esa lista. Si la función modifica un parámetro de la lista, el código que la ha
llamado también se verá afectado por el cambio. Por ejemplo,
borra_primer elimina el primer elemento de una lista:
def borra_primer(t):
del t[0]
Resulta importante distinguir entre las operaciones que modifican listas y las
operaciones que crean listas nuevas. Por ejemplo, el método append
modifica una lista, pero el operador + crea una lista nueva:
>>> t1 = [1, 2]
>>> t2 = t1.append(3)
>>> print t1
[1, 2, 3]
>>> print t2
None
>>> t3 = t1 + [3]
>>> print t3
[1, 2, 3]
>>> t2 is t3
False
def no_borra_primer(t):
t = t[1:] # ¡INCORRECTO!
Una alternativa consiste en escribir una función que cree y retorne una lista
nueva. Por ejemplo, cola devuelve todos los elementos de la lista excepto el
primero:
def cola(t):
return t[1:]
Esta función deja la lista original sin modificar. Aquí está el modo como se
usa:
Ejercicio 1
Escribe una función llamada recorta, que tome una lista, la modifique,
eliminando los elementos primero y último, y devuelva None.
Después escribe una función llamada centro, que tome una lista y
devuelva otra que contenga todos los elementos de la original, menos el
primero y el último.
8.14 Depuración
palabra = palabra.strip()
t = t.sort() # ¡INCORRECTO!
Parte del problema con las listas es que hay demasiados modos de
hacer las cosas. Por ejemplo, para eliminar un elemento de una lista, se
puede usar pop, remove, del, o incluso una asignación de rebanada.
t.append(x)
t = t + [x]
t.append([x]) # ¡INCORRECTO!
t = t.append(x) # ¡INCORRECTO!
t + [x] # ¡INCORRECTO!
t = t + x # ¡INCORRECTO!
orig = t[:]
t.sort()
manf = open('mbox-short.txt')
for linea in manf:
palabras = linea.split()
if palabras[0] != 'From' : continue
print palabras[2]
python search8.py
Sat
Traceback (most recent call last):
File "search8.py", line 5, in <module>
if palabras[0] != 'From' : continue
IndexError: list index out of range
Puedes revisarlo durante largo rato y romperte la cabeza con él, o pedir
ayuda a alguien, pero el enfoque más rápido e inteligente consiste en
añadir una sentencia print. El mejor lugar para situarla es justo antes
de la línea en la que falla el programa, e imprimir el dato que parecer
ser el causante del error.
X-DSPAM-Result: Innocent
X-DSPAM-Processed: Sat Jan 5 09:14:16 2008
X-DSPAM-Confidence: 0.8475
X-DSPAM-Probability: 0.0000
Details: http://source.sakaiproject.org/viewsvn/?
view=rev&rev=39772
manf = open('mbox-short.txt')
contador= 0
for linea in manf:
palabras = linea.split()
# print 'Debug:', palabras
if len(palabras) == 0 : continue
if palabras[0] != 'From' : continue
print palabras[2]
Ejercicio 2
8.15 Glosario
alias:
Una circunstancia en la cual dos o más variables se refieren al mismo
objeto.
delimitador:
Un carácter o cadena usado para indicar por dónde debe ser dividida
una cadena.
elemento:
Uno de los valores en una lista (u otra secuencia); también reciben el
nombre de artículos.
equivalentes:
Que tienen el mismo valor.
idénticos:
Que son el mismo objeto (lo cual implica equivalencia).
índice:
Un valor entero que indica un elemento concreto dentro de una lista.
lista:
Una secuencia de valores.
lista anidada:
Una lista que es un elemento de otra lista.
objeto:
Algo a lo que se puede referir una variable. Un objeto tiene un tipo y un
valor.
recorrido de una lista:
El acceso secuencial a cada elemento de una lista.
referencia:
La asociación entre una variable y su valor.
8.16 Ejercicios
python fromcount.py
Introduce un nombre de fichero: mbox-short.txt
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
Hay 27 lineas en el archivo con From como primera
palabra
Introduce un número: 6
Introduce un número: 2
Introduce un número: 9
Introduce un número: 3
Introduce un número: 5
Introduce un número: fin
Máximo: 9.0
Mínimo: 2.0
Chapítulo 9 Diccionarios
Un diccionario es similar a una lista, pero más general. En una lista, las
posiciones de los índices deben ser enteros; en un diccionario, los índices
pueden ser de (casi) cualquier tipo.
La función dict crea un diccionario nuevo sin elementos. Dado que dict es
el nombre de una función interna, debes evitar usarla como nombre de
variable.
Esta línea crea un elemento con la clave ’one’ que apunta al valor 'uno'. Si
imprimimos el diccionario de nuevo, veremos una pareja clave-valor con
dos-puntos entre la clave y el valor:
La clave ’two’ siempre apunta al valor 'dos', de modo que el orden de los
elementos no importa.
>>> len(eng2sp)
3
El operador in también funciona en los diccionarios; te dice si algo aparece
como clave en el diccionario (que aparezca como valor no es suficiente).
Ejercicio 1
1. Podrías crear 26 variables, una para cada letra del alfabeto. Después,
podrías recorrer la cadena y, para cada carácter, aumentar el contador
correspondiente, probablemente usando un condicional encadenado.
2. Podrías crear una lista con 26 elementos. Luego podrías convertir cada
carácter en un número (usando la función interna ord), usar el número
como índice dentro de la lista, y aumentar el contador apropiado.
3. Podrías crear un diccionario con los caracteres como claves y
contadores como sus valores correspondientes. La primera vez que veas
un carácter, añadirías un elemento al diccionario. Después, aumentarías
el valor del elemento ya existente.
Todas estas opciones realizan la misma operación, pero cada una de ellas
implementa esa operación de un modo diferente.
palabra = 'brontosaurio'
d = dict()
for c in palabra:
if c not in d:
d[c] = 1
else:
d[c] = d[c] + 1
print d
El histograma indica que las letras ’a’ y 'b' aparecen una vez; 'o' aparece
tres, y así con las demás.
Los diccionarios tienen un método llamado get que toma una clave y un valor
por defecto. Si la clave aparece en el diccionario, get devuelve el valor
correspondiente; si no, devuelve el valor por defecto. Por ejemplo:
Podemos usar get para escribir nuestro bucle de histograma de forma más
concisa. Como el método get controla automáticamente el caso de que la
clave no esté en el diccionario, podemos reducir cuatro líneas a una sola y
eliminar la sentencia if
palabra = 'brontosaurio'
d = dict()
for c in palabra:
d[c] = d.get(c,0) + 1
print d
El uso del método get para simplificar este bucle de recuento al final resulta
ser un “estilo” que se usa en Python con mucha frecuencia, y lo utilizaremos
muchas veces en el resto del libro. Así que deberías pararte un momento y
comparar el bucle usando la sentencia if y el operador in con el mismo bucle
usando el método get. Hacen exactamente lo mismo, pero uno es más conciso.
Verás que tenemos dos bucles for. El bucle exterior va leyendo las líneas del
archivo, mientras que el interior va iterando a través de cada una de las
palabras de una línea concreta. Esto es un ejemplo de un diseño llamado
bucles anidados, ya que uno de los bucles es el exterior, y el otro es el
interior.
Debido a que el bucle interior ejecuta todas sus iteraciones cada vez que el
bucle exterior realiza una sola, consideramos que el bucle interior va iterando
“más rápido” y que el exterior lo hace más lentamente.
La combinación de los dos bucles anidados garantiza que se cuentan todas las
palabras en cada línea del archivo de entrada.
contadores = dict()
for linea in manf:
palabras = linea.split()
for palabra in palabras:
if palabra not in contadores:
contadores[palabra] = 1
else:
contadores[palabra] += 1
print contadores
python count1.py
Introduce el nombre del fichero: romeo.txt
{'and': 3, 'envious': 1, 'already': 1, 'fair': 1,
'is': 3, 'through': 1, 'pale': 1, 'yonder': 1,
'what': 1, 'sun': 2, 'Who': 1, 'But': 1, 'moon': 1,
'window': 1, 'sick': 1, 'east': 1, 'breaks': 1,
'grief': 1, 'with': 1, 'light': 1, 'It': 1, 'Arise': 1,
'kill': 1, 'the': 3, 'soft': 1, 'Juliet': 1}
Resulta un poco incómodo buscar a través del diccionario para encontrar cuál
es la palabra más común y su contador, de modo que necesitamos añadir un
poco más de código Phyton que nos proporcione la salida de un modo que nos
resulte más útil.
9.3 Bucles y diccionarios
jan 100
chuck 1
annie 42
Podemos usar este modelo para poner en práctica las diversas expresiones de
bucles que hemos descrito antes. Por ejemplo, si queremos encontrar todas las
entradas de un diccionario que tengan un valor superior a diez, podríamos
escribir el siguiente código:
El bucle for itera a través de las claves del diccionario, de modo que
podemos usar el operador índice para recuperar el valor correspondiente
para cada clave. Aquí podemos ver el aspecto de la salida:
jan 100
annie 42
Elimina todos los caracteres de s que hay en deletechars (si existe alguno),
y luego traduce los caracteres usando table, que debe ser una cadena de
256-caracteres que proporcione la traducción para cada valor de carácter,
indexado por su ordinal. Si la tabla es None, entonces sólo se realizará el
borrado de caracteres.
import string #
Código nuevo
contadores = dict()
for linea in nombref:
linea = linea.translate(None, string.punctuation)
# Código nuevo
linea = linea.lower()
# Código nuevo
palabras = linea.split()
for palabra in palabras:
if palabra not in palabras:
contadores[palabra] = 1
else:
contadores[palabra] += 1
print contadores
9.5 Depuración
Reduce la entrada:
Si es posible, reduce el tamaño del conjunto de datos. Por ejemplo, si el
programa lee un archivo de texto, comienza solamente con las primeras
10 líneas, o con el ejemplo más pequeño que puedas encontrar. Puedes
editar los propios archivos, o (mejor) modificar el programa de modo
que lea sólo las primeras n líneas.
Escribe auto-comprobaciones:
A veces puedes escribir código que compruebe los errores
automáticamente. Por ejemplo, si estás calculando la media de una lista
de números, puedes comprobar que el resultado no es mayor que el
elemento más grande de la lista, o menor que el más pequeño. A eso se le
llama “sanity check (prueba de sensatez)”, porque detecta resultados que
son “completamente ilógicos”.
Una vez más, el tiempo que emplees construyendo la estructura puede reducir
el tiempo que emplearás luego depurando.
9.6 Glosario
bucles anidados:
Cuando hay uno o más bucles “dentro” de otro bucle. El bucle interior se
ejecuta completo cada vez que el exterior se ejecuta una vez.
búsqueda (lookup):
Una operación en un diccionario que toma una clave y encuentra su valor
correspondiente.
clave:
Un objeto que aparece en un diccionario como la primera parte de una
pareja clave-valor.
diccionario:
Un mapeo de un conjunto de claves hacia sus valores correspondientes.
elemento:
Otro nombre para una pareja clave-valor.
función de dispersión (hash function):
Una función usada por una tabla de dispersión para calcular la
localización de una clave.
histograma:
Un conjunto de contadores.
implementación:
Un modo de realizar un cálculo.
pareja clave-valor:
La representación del mapeado desde una clave hacia un valor.
tabla de dispersión (hashtable):
El algoritmo usado para implementar los diccionarios de Python.
valor:
Un objeto que aparece en un diccionario como la segunda parte de una
pareja clave-valor. Es más específico que el uso que hacíamos antes de
la palabra “valor”.
9.7 Ejercicios
Línea de ejemplo:
From [email protected] Sat Jan 5 09:14:16 2008
Ejemplo de Ejecución:
python dow.py
Intoduce un nombre de fichero: mbox-short.txt
{'Fri': 20, 'Thu': 6, 'Sat': 1}
Ejercicio 3 Escribe un programa que lea a través de un registro de correo,
construya un histograma usando un diccionario para contar cuántos
mensajes han llegado desde cada dirección de correo, e imprima el
diccionario.
Después de que los datos hayan sido leídos y el diccionario haya sido
creado, busca a través del diccionario usando un bucle máximo (mira la
Section ??) para encontrar quién es el que tiene más mensajes e imprime
cuántos mensajes tiene esa persona.
python schoolcount.py
Introduce un nombre de fichero: mbox-short.txt
{'media.berkeley.edu': 4, 'uct.ac.za': 6, 'umich.edu':
7,
'gmail.com': 1, 'caret.cam.ac.uk': 1, 'iupui.edu': 8}
Chapítulo 10 Tuplas
Una tupla1 es una secuencia de valores muy parecida a una lista. Los valores
almacenados en una tupla pueden ser de cualquier tipo, y están indexados
por enteros. La diferencia más importante es que las tuplas son inmutables.
Las tuplas además son comparables y hashables (dispersables), de modo
que las listas de tuplas se pueden ordenar y también es posible usar tuplas
como valores para las claves en los diccionarios de Python.
Para crear una tupla con un único elemento, es necesario incluir una coma al
final:
>>> t1 = ('a',)
>>> type(t1)
<type 'tuple'>
Sin la coma, Python trata ('a') como una expresión con una cadena dentro
de un paréntesis, que evalúa como de tipo “string”:
>>> t2 = ('a')
>>> type(t2)
<type 'str'>
Otro modo de construir una tupla es usar la función interna tuple. Sin
argumentos, crea una tupla vacía:
>>> t = tuple()
>>> print t
()
>>> t = tuple('altramuces')
>>> print t
('a','l', 't', 'r', 'a', 'm', 'u', 'c', 'e', 's')
Decorate
(Decora) una secuencia, construyendo una lista de tuplas con uno o más
índices ordenados precediendo los elementos de dicha secuencia,
Sort
(Ordena) la lista de tuplas usando la función incorporada en Python
sort, y
Undecorate
(Quita la decoración), extrayendo los elementos ordenados de la
secuencia.
Por ejemplo, supón que tienes una lista de palabras y que quieres ordenarlas
de más larga a más corta:
t.sort(reverse=True)
res = list()
for longitud, palabra in t:
res.append(palabra)
print res
El primer bucle crea una lista de tuplas, en la que cada tupla es la palabra
precedida por su longitud.
Una de las características sintácticas del lenguaje Python que resulta única
es la capacidad de tener una tupla en el lado izquierdo de una sentencia de
asignación. Esto permite asignar varias variables el mismo tiempo cuando
tenemos una secuencia en el lado izquierdo.
En este ejemplo tenemos una lista de dos elementos (por lo que se trata de
una secuencia), y asignamos los elementos primero y segundo de la
secuencia a las variables x e y en una única sentencia.
>>> a, b = b, a
Ambos lados de esta sentencia son tuplas, pero el lado izquierdo es una tupla
de variables; el lado derecho es una tupla de expresiones. Cada valor en el
lado derecho es asignado a su respectiva variable en el lado izquierdo.
Todas las expresiones en el lado derecho son evaluadas antes de realizar
ninguna asignación.
>>> a, b = 1, 2, 3
ValueError: too many values to unpack
Los diccionarios tienen un método llamado items que devuelve una lista de
tuplas, cada una de las cuales es una pareja clave-valor 3.
Sin embargo, dado que la lista de tuplas es una lista, y las tuplas son
comparables, ahora podemos ordenar la lista de tuplas. Convertir un
diccionario en una lista de tuplas es un método para obtener el contenido de
un diccionario ordenado según sus claves:
Este bucle tiene dos variables de iteración, ya que items devuelve una lista
de tuplas y clave, valor es una asignación en tupla, que itera
sucesivamente a través de cada una de las parejas clave-valor del
diccionario.
Para cada iteración a través del bucle, tanto clave como valor van pasando
a la siguiente pareja clave-valor del diccionario (aquí también en orden de
dispersión).
10 a
22 c
1 b
Para conseguirlo, primero creamos una lista de tuplas, donde cada tupla es
(valor, clave). El método items nos dará una lista de tuplas (clave,
valor)—pero esta vez queremos ordenar por valor, no por clave. Una vez
que hayamos construido la lista con las tuplas clave-valor, resulta sencillo
ordenar la lista en orden inverso e imprimir la nueva lista ordenada.
>>> d = {'a':10, 'b':1, 'c':22}
>>> l = list()
>>> for clave, valor in d.items() :
... l.append( (valor, clave) )
...
>>> l
[(10, 'a'), (22, 'c'), (1, 'b')]
>>> l.sort(reverse=True)
>>> l
[(22, 'c'), (10, 'a'), (1, 'b')]
>>>
Volviendo a nuestro ejemplo anterior del texto de Romeo and Juliet Acto 2,
Escena 2, podemos mejorar nuestro programa para hacer uso de esta técnica
e imprimir las diez palabras más comunes en el texto, como vemos a
continuación:
import string
manf = open('romeo-full.txt')
contadores = dict()
for linea in manf:
linea = linea.translate(None, string.punctuation)
linea = linea.lower()
palabras = linea.split()
for palabra in palabras:
if palabra not in contadores:
contadores[palabra] = 1
else:
contadores[palabra] += 1
# Ordenar el diccionario por valor
lst = list()
for clave, valor in contadores.items():
lst.append( (valor, clave) )
lst.sort(reverse=True)
Al final escribimos un bonito bucle for que hace una iteración con
asignación múltiple e imprime en pantalla las diez palabras más comunes,
iterando a través de una rebanada de la lista (lst[:10]).
61 i
42 and
40 romeo
34 to
34 the
32 thou
32 juliet
30 that
29 my
24 thee
El hecho de que este complejo procesado y análisis de datos puedan ser
realizado con un programa Python de 19 líneas sencillo de entender, es una
de las razones por las que Python es una buena elección como lenguaje para
explorar información.
Dado que las tuplas son hashables (dispersables) y las listas no, si
queremos crear una clave compuesta para usar en un diccionario,
deberemos usar una tupla como clave.
directorio[apellido,nombre] = numero
Este bucle recorre las claves de directorio, que son tuplas. Asigna los
elementos de cada tupla a apellido y nombre, luego imprime el nombre,
apellido y número de teléfono correspondiente.
Me he centrado en las listas y tuplas, pero casi todos los ejemplos de este
capítulo funcionan también en listas de listas, tuplas de tuplas y tuplas de
listas. Para evitar enumerar todas las combinaciones posibles, a veces
resulta más sencillo hablar de secuencias de secuencias.
Para comenzar con lo más obvio, las cadenas están más limitadas que las
demás secuencias, porque los elementos deben ser caracteres. También son
inmutables. Si necesitas la capacidad de cambiar los caracteres en una
cadena (en vez de crear una nueva), puede que lo más adecuado sea elegir
una lista de caracteres.
Las listas se usan con más frecuencia que las tuplas, principalmente porque
son mutables. Pero hay algunos pocos casos donde es posible que prefieras
usar las tuplas:
Dado que las tuplas son inmutables, no proporcionan métodos como sort y
reverse, que modifican listas ya existentes. Sin embargo, Python
proporciona las funciones integradas sorted y reversed, que toman una
secuencia como parámetro y devuelven una secuencia nueva con los mismos
elementos en un orden diferente.
10.9 Depuración
lectura:
Examina tu código, léelo para ti, y comprueba si en realidad dice lo que
querías que dijera.
ejecución:
Experimenta haciendo cambios y ejecutando versiones diferentes. A
menudo, si muestras las cosas correctas en los lugares adecuados del
programa el problema se convierte en obvio, pero otras veces tendrás
que invertir algún tiempo construyendo unas ciertas estructuras.
rumiado:
¡Tómate tu tiempo para reflexionar! ¿De qué tipo de error se trata:
sintáctico, de ejecución, semántico? ¿Qué información puedes obtener
de los mensajes de error, o de la salida del programa? ¿Qué tipo de
error podría causar el problema que estás viendo? ¿Qué fue lo último
que cambiaste, antes de que el problema apareciera?
retirada:
En algunos casos, lo mejor que se puede hacer es dar marcha atrás,
deshaciendo los últimos cambios, hasta llegar a un punto en que el
programa funcione y tú seas capaz de entenderlo. A partir de ahí, puedes
comenzar a reconstruirlo.
Los programadores novatos a veces se quedan atascados en una de estas
actividades y olvidan las otras. Cada actividad cuenta con su propio tipo de
fracaso.
10.10 Glosario
asignación en tupla:
Una asignación con una secuencia en el lado derecho y una tupla de
variables en el izquierdo. Primero se evalúa el lado derecho y luego sus
elementos son asignados a las variables de la izquierda.
comparable:
Un tipo en el cual un valor puede ser contrastado para ver si es mayor
que, menor que, o igual que otro valor del mismo tipo. Los tipos que son
comparables pueden ser puestos en una lista y ordenados.
estructura de datos:
Una colección de valores relacionados, a menudo organizados en listas,
diccionarios, tuplas, etc.
DSU:
Abreviatura de “decorate-sort-undecorate (decorar-ordenar-quitar la
decoración)”, un patrón que implica construir una lista de tuplas,
ordenar, y extraer parte del resultado.
hashable (dispersable):
Un tipo que tiene una función de dispersión. Los tipos inmutables, como
enteros, flotantes y cadenas son hashables (dispersables); los tipos
mutables como listas y diccionarios no lo son.
dispersar:
La operación de tratar una secuencia como una lista de argumentos.
modelado (de una estructura de datos):
Un resumen del tipo, tamaño, y composición de una estructura de datos.
reunir:
La operación de montar una tupla como argumento de longitud variable.
singleton:
Una lista (u otra secuencia) con un único elemento.
tupla:
Una secuencia inmutable de elementos.
10.11 Ejercicios
Ejercicio 1 Revisa el ejercicio 9.3, del tema anterior, de este modo: Lee y
procesa las líneas “From” y extrae la dirección. Cuenta el número de
mensajes de cada persona usando un diccionario. Después de que todos
los datos hayan sido leídos, para mostrar la persona con más envíos, crea
una lista de tuplas (contador, email) desde el diccionario. Luego ordena la
lista en orden inverso y muestra la persona que tiene más envíos.
Línea de ejemplo:
From [email protected] Sat Jan 5 09:14:16
2008
Ejecución de ejemplo:
python timeofday.py
Introduce un nombre de fichero: mbox-short.txt
04 3
06 1
07 1
09 2
10 3
11 6
14 1
15 2
16 4
17 2
18 1
19 1
1
Anécdota: La palabra “tupla (tuple en inglés)” proviene de los nombres
dados a las secuencias de números de distintas longitudes: simple,
doble, triple, cuádrupe, quíntuple, séxtuple, séptuple, etc.
2
Python no convierte la sintaxis de forma literal. Por ejemplo, si intentas
esto con un diccionario, no funcionará como esperarías.
3
Este comportamiento es ligeramente diferente en Python 3.0.
Chapítulo 11 Expresiones regulares
Esta tarea de buscar y extraer es tan común que Python tiene una librería muy
potente llamada expresiones regulares, que se encarga de muchas de estas
tareas de forma elegante. La razón por la que no hemos introducido las
expresiones regulares antes en este libro se debe a que, a pesar de que son
muy potentes, también son un poco complicadas y lleva algún tiempo
acostumbrarse a su sintaxis.
http://es.wikipedia.org/wiki/Expresion_regular
https://docs.python.org/2/library/re.html
import re
manf = open('mbox-short.txt')
for linea in manf:
linea = linea.rstrip()
if re.search('From:', linea) :
print linea
Abrimos el fichero, vamos recorriendo cada línea, y usamos la expresión
regular search() para imprimir solamente aquellas líneas que contienen la
cadena “From:”. Este programa no usa la potencia real de las expresiones
regulares, ya que podríamos haber usado simplemente linea.find() para
lograr el mismo resultado.
import re
manf = open('mbox-short.txt')
for linea in manf:
linea = linea.rstrip()
if re.search('^From:', linea) :
print linea
Ahora sólo coincidirán las líneas que comiencen por la cadena “From:”.
Todavía se trata de un ejemplo de análisis muy sencillo, ya que podríamos
haber hecho lo mismo con el método startswith() de la librería de
cadenas. Pero sirve para introducir la noción de que las expresiones
regulares contienen caracteres de acción especiales que nos dan más control
sobre lo que localizará la expresión regular.
Hay varios caracteres especiales más que nos permiten construir expresiones
regulares aún más potentes. El más usado de ellos es el punto o parada
completa, que equivale a cualquier carácter.
En el ejemplo siguiente, la expresión regular “F..m:” coincidirá con
cualquiera de las cadenas “From:”, “Fxxm:”, “F12m:”, or “F!@m:”, ya que
el carácter punto en la expresión regular equivale a cualquier carácter.
import re
manf = open('mbox-short.txt')
for linea in manf:
linea = linea.rstrip()
if re.search('^F..m:', linea) :
print linea
Podemos restringir aún más las líneas que coincidirán con la búsqueda,
usando un carácter comodín que se repita, como en el ejemplo siguiente:
import re
manf = open('mbox-short.txt')
for linea in manf:
linea = linea.rstrip()
if re.search('^From:.+@', linea) :
print linea
Puedes pensar en el comodín “.+” como una extensión que coincide con
todos los caracteres entre los dos-puntos y el símbolo arroba.
From:.+ @
No vamos a escribir código para cada uno de los tipos de líneas, dividirlas y
rebanarlas de forma diferente en cada caso. El programa siguiente usa
findall() para localizar las líneas que contienen direcciones de e-mail, y
extraer una o más direcciones de cada una de esas líneas.
import re
s = 'Hello from [email protected] to [email protected] about
the meeting @2PM'
lst = re.findall('\S+@\S+', s)
print lst
['[email protected]', '[email protected]']
import re
manf = open('mbox-short.txt')
for linea in manf:
linea = linea.rstrip()
x = re.findall('\S+@\S+', linea)
if len(x) > 0 :
print x
Vamos leyendo cada línea y luego extraemos todas las subcadenas que
coinciden con nuestra expresión regular. Como findall() devuelve una
lista, simplemente comprobamos si el número de elementos en la lista de
retorno es mayor que cero, para mostrar sólo las líneas en las cuales hemos
encontrado al menos una subcadena que parece una dirección de e-mail.
['[email protected]']
['[email protected]']
['<[email protected]>']
['<[email protected]>
']
['<[email protected]>;']
['<[email protected]>;']
['<[email protected]>;']
['apache@localhost)']
['[email protected];']
[a-zA-Z0-9]\S*@\S*[a-zA-Z]
Esto se va volviendo un poco complicado y seguramente ya empiezas a ver
por qué las expresiones regulares tienen su propio lenguaje para ellas solas.
Traduciendo esta expresión regular, estamos buscando subcadenas que
comiencen con una única letra, en minúsculas o mayúsculas, o un número
“[a-zA-Z0-9]”, seguido por cero o más caracteres no-espacios-en-blanco
(“\S*”), seguido por un símbolo arroba, seguido por cero o más caracteres
no-espacios-en-blanco (“\S*”), seguido por una letra en mayúsculas o
minúsculas. Fíjate que hemos cambiado de “+” a “*” para indicar cero o más
caracteres no-blancos, dado que “[a-zA-Z0-9]” ya es un carácter no-blanco.
Recuerda que el “*” o “+” se aplica al carácter que queda inmediatamente a
la izquierda del más o el asterisco.
Si usamos esta expresión en nuestro programa, los datos quedan mucho más
limpios:
import re
manf = open('mbox-short.txt')
for linea in manf:
linea = linea.rstrip()
x = re.findall('[a-zA-Z0-9]\S*@\S*[a-zA-Z]', linea)
if len(x) > 0 :
print x
...
['[email protected]']
['[email protected]']
['[email protected]']
['[email protected]']
['[email protected]']
['[email protected]']
['[email protected]']
['apache@localhost']
Fíjate también que la salida de este programa tiene, en cada línea, una lista
de Python que contiene una cadena como único elemento.
X-DSPAM-Confidence: 0.8475
X-DSPAM-Probability: 0.0000
^X-.*: [0-9.]+
Se trata de una expresión muy rigurosa, que localizará bastante bien sólo las
líneas en las que estamos interesados:
import re
manf = open('mbox-short.txt')
for linea in manf:
linea = linea.rstrip()
if re.search('^X\S*: [0-9.]+', linea) :
print linea
X-DSPAM-Confidence: 0.8475
X-DSPAM-Probability: 0.0000
X-DSPAM-Confidence: 0.6178
X-DSPAM-Probability: 0.0000
import re
manf = open('mbox-short.txt')
for linea in manf:
linea = linea.rstrip()
x = re.findall('^X\S*: ([0-9.]+)', linea)
if len(x) > 0 :
print x
['0.8475']
['0.0000']
['0.6178']
['0.0000']
['0.6961']
['0.0000']
..
Details: http://source.sakaiproject.org/viewsvn/?
view=rev&rev=39772
import re
manf = open('mbox-short.txt')
for linea in manf:
linea = linea.rstrip()
x = re.findall('^Details:.*rev=([0-9]+)', linea)
if len(x) > 0:
print x
Traduciendo nuestra expresión regular, estamos buscando aquellas líneas que
comiencen con “Details:”, seguido de cualquier número de caracteres (“*”),
seguido por “rev=”, y luego por uno o más dígitos. Queremos encontrar las
líneas que coincidan con la expresión completa, pero sólo queremos extraer
el número entero al final de la línea, de modo que rodeamos con paréntesis
“[0-9]+”.
['39772']
['39771']
['39770']
['39769']
...
^From .* [0-9][0-9]:
^From .* ([0-9][0-9]):
import re
manf = open('mbox-short.txt')
for linea in manf:
linea = linea.rstrip()
x = re.findall('^From .* ([0-9][0-9]):', linea)
if len(x) > 0 : print x
['09']
['18']
['16']
['15']
...
import re
x = 'Acabamos de recibir $10.00 por las galletas.'
y = re.findall('\$[0-9.]+',x)
Dado que hemos antepuesto una barra invertida al signo dólar, ahora
equivaldrá al símbolo del dolar en la cadena de entrada, en lugar de
equivaler al “final de la línea”, y el resto de la expresión regular buscará
uno o más dígitos o el carácter punto. Nota: Dentro de los corchetes, los
caracteres no son “especiales”. De modo que cuando escribimos “[0-9.]”, en
realidad significa dígitos o un punto. Fuera de los corchetes, un punto es el
carácter “comodín” y coincide con cualquier carácter. Dentro de los
corchetes, el punto es simplemente un punto.
11.5 Resumen
$
Coincide con el final de una línea.
.
Coincide con cualquier carácter (un comodín).
\s
Coincide con un carácter espacio en blanco.
\S
Coincide con cualquier carácter que no sea un espacio en blanco (opuesto a
\s).
*
Se aplica al carácter que le precede e indica que la búsqueda debe coincidir
cero o más veces con él.
*?
Se aplica al carácter que le precede e indica que la búsqueda debe coincidir
cero o más veces con él en “modo no-codicioso”.
+
Se aplica al carácter que le precede e indica que la búsqueda debe coincidir
una o más veces con él.
+?
Se aplica al carácter que le precede e indica que la búsqueda debe coincidir
una o más veces con él en “modo no-codicioso”.
[aeiou]
Coincide con un único carácter siempre que ese carácter esté en el conjunto
especificado. En este ejemplo, deberían coincidir “a”, “e”, “i”, “o”, o “u”,
pero no los demás caracteres.
[a-z0-9]
Se pueden especificar rangos de caracteres usando el signo menos. Este
ejemplo indica un único carácter que puede ser una letra minúscula o un
dígito.
[^A-Za-z]
Cuando el primer carácter en la notación del conjunto es un símbolo de
intercalación, se invierte la lógica. En este ejemplo, coincide con un único
carácter que sea cualquier cosa excepto una letra mayúscula o minúscula.
()
Cuando se añaden paréntesis a una expresión regular, éstos son ignorados a
efectos de la búsqueda, pero permiten extraer un subconjunto particular de la
cadena localizada en vez de la cadena completa, cuando usamos findall().
\b
Coincide con la cadena vacía, pero sólo al principio o al final de una
palabra.
\B
Coincide con la cadena vacía, pero no al principio o al final de una palabra.
\d
Coincide con cualquier dígito decimal, es equivalente al conjunto [0-9].
\D
Coincide con cualquier carácter que no sea un dígito; equivale al conjunto
[^0-9].
Esto le dice a grep que muestre las líneas que comienzan con la cadena
“From:” del archivo mbox-short.txt. Si experimentas un poco con el
comando grep y lees su documentación, encontrarás algunas sutiles
diferencias entre el soporte de expresiones regulares en Python y el de grep.
Por ejemplo, grep no soporta el carácter equivalente a no-espacio-en-
blanco, “\S”, de modo que hay que usar la notación bastante más compleja
“[^ ]”, que simplemente significa que busque un carácter que sea cualquier
cosa distinta a un espacio.
11.7 Depuración
>>> help()
help> modules
Si sabes qué módulo quieres usar, puedes utilizar el comando dir() para
localizar los métodos del módulo, como se muestra a continuación:
>>> import re
>>> dir(re)
[.. 'compile', 'copy_reg', 'error', 'escape',
'findall',
'finditer', 'match', 'purge', 'search', 'split',
'sre_compile',
'sre_parse', 'sub', 'subn', 'sys', 'template']
11.8 Glosario
código frágil:
Código que funciona cuando los datos de entrada tienen un formato
particular, pero es propenso a fallar si hay alguna desviación del
formato correcto. Llamamos a eso “código frágil”, porque “se rompe”
con facilidad.
coincidencia codiciosa:
El concepto de que los caracteres “+” y “*” de una expresión regular se
expanden hacia fuera para capturar la cadena más larga posible.
comodín:
Un carácter especial que coincide con cualquier carácter. En las
expresiones regulares, el carácter comodín es el punto.
expresión regular:
Un lenguaje para expresiones de búsqueda de cadenas más complejo.
Una expresión regular puede contener caracteres especiales que
indiquen que una búsqueda sólo coincida con el principio o el final de
una línea o muchas otras capacidades similares.
grep:
Un comando disponible en la mayoría de sistemas Unix que busca a
través de archivos de texto, localizando líneas que coincidan con una
expresión regular. El nombre del comando significa "Generalized
Regular Expression Parser" (Analizador Generalizado de Expresiones
Regulares).
11.9 Ejercicios
Ejercicio 1 Escribe un programa sencillo que simule la forma de operar
del comando grep de Unix. Pide al usuario introducir una expresión
regular y cuenta el número de líneas que localiza a partir de ella:
$ python grep.py
Introduce una expresión regular: ^Author
mbox.txt tiene 1798 líneas que coinciden con ^Author
$ python grep.py
Introduce una expresión regular: ^X-
mbox.txt tiene 14368 líneas que coinciden con ^X-
$ python grep.py
Introduce una expresión regular: java$
mbox.txt tiene 4218 líneas que coinciden con java$
Introduce fichero:mbox.txt
38549.7949721
Introduce fichero:mbox-short.txt
39756.9259259
Chapítulo 12 Programas en red
Pero si intentas leer de un socket cuando el programa que está al otro lado no
ha enviado ningún dato—puedes esperar sentado. Si los programas de ambos
extremos del socket simplemente intentan recibir datos sin que ninguno envíe
nada, esperarán durante mucho, mucho tiempo.
http://www.w3.org/Protocols/rfc2616/rfc2616.txt
Tal vez el modo más fácil de mostrar cómo funciona el protocolo HTTP sea
escribir un programa en Python muy sencillo, que realice una conexión con
un servidor web y siga las reglas de ese protocolo para solicitar un
documento y mostrar lo que el servidor le devuelve.
import socket
misock = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
misock.connect(('www.py4inf.com', 80))
misock.send('GET http://www.py4inf.com/code/romeo.txt
HTTP/1.0\n\n')
while True:
datos = misock.recv(512)
if ( len(datos) < 1 ) :
break
print datos
misock.close()
Una vez enviada esa línea en blanco, escribimos un bucle que recibe los
datos desde el socket en bloques de 512 caracteres y los imprime en pantalla
hasta que no quedan más datos por leer (es decir, hasta que recv() devuelve
una cadena vacía).
HTTP/1.1 200 OK
Date: Sun, 14 Mar 2010 23:52:41 GMT
Server: Apache
Last-Modified: Tue, 29 Dec 2009 01:31:22 GMT
ETag: "143c1b33-a7-4b395bea"
Accept-Ranges: bytes
Content-Length: 167
Connection: close
Content-Type: text/plain
But soft what light through yonder window breaks
It is the east and Juliet is the sun
Arise fair sun and kill the envious moon
Who is already sick and pale with grief
La salida comienza con las cabecera que el servidor web envía para
describir el documento. Por ejemplo, la cabecera Content-Type indica que
el documento es del tipo texto plano (text/plain).
Después de que el servidor nos envía la cabecera, añade una línea en blanco
para indicar el final de la misma, y a continuación envía los datos reales del
fichero romeo.txt.
Este ejemplo nos muestra cómo crear una conexión de red de bajo nivel con
sockets. Los sockets pueden ser usados para comunicarse con un servidor
web, con un servidor de correo o con muchos otros tipos de servidores.
Todo lo que se necesita es localizar el documento que describe el protocolo
correspondiente y escribir el código para enviar y recibir los datos de
acuerdo a ese protocolo.
import socket
import time
misock = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
misock.connect(('www.py4inf.com', 80))
misock.send('GET http://www.py4inf.com/cover.jpg
HTTP/1.0\n\n')
contador = 0
imagen = "";
while True:
datos = misock.recv(5120)
if ( len(datos) < 1 ) : break
# time.sleep(0.25)
contador = contador + len(datos)
print len(datos),contador
imagen = imagen + datos
misock.close()
$ python urljpeg.py
2920 2920
1460 4380
1460 5840
1460 7300
...
1460 62780
1460 64240
2920 67160
1460 68620
1681 70301
Longitud de cabecera 240
HTTP/1.1 200 OK
Date: Sat, 02 Nov 2013 02:15:07 GMT
Server: Apache
Last-Modified: Sat, 02 Nov 2013 02:01:26 GMT
ETag: "19c141-111a9-4ea280f8354b8"
Accept-Ranges: bytes
Content-Length: 70057
Connection: close
Content-Type: image/jpeg
Puedes observar que, para esta url, la cabecera Content-Type indica que el
cuerpo del documento es una imagen (image/jpeg). Una vez que el
programa se completa, se pueden ver los datos de la imagen abriendo el
archivo cosa.jpg con un visor de imágenes.
$ python urljpeg.py
1460 1460
5120 6580
5120 11700
...
5120 62900
5120 68020
2281 70301
Longitud de cabecera 240
HTTP/1.1 200 OK
Date: Sat, 02 Nov 2013 02:22:04 GMT
Server: Apache
Last-Modified: Sat, 02 Nov 2013 02:01:26 GMT
ETag: "19c141-111a9-4ea280f8354b8"
Accept-Ranges: bytes
Content-Length: 70057
Connection: close
Content-Type: image/jpeg
Ahora todas las llamadas a recv(), excepto la primera y la última, nos dan
5120 caracteres cada vez que solicitamos más datos.
Existe un buffer entre el servidor que hace las peticiones send() y nuestra
aplicación que hace las peticiones recv(). Cuando ejecutamos el programa
con el retraso activado, en algún momento el servidor podría llenar el buffer
del socket y verse forzado a detenerse hasta que nuestro programa empiece a
vaciar ese buffer. La detención de la aplicación que envía los datos o de la
que los recibe se llama “control de flujo”.
Al usar urllib, es posible tratar una página web de forma mucho más
parecida a un fichero. Se puede indicar simplemente qué página web se
desea recuperar y urllib se encargará de gestionar todo lo referente al
protocolo HTTP y los detalles de la cabecera.
import urllib
manf =
urllib.urlopen('http://www.py4inf.com/code/romeo.txt')
for linea in manf:
print linea.strip()
Una vez que la página web ha sido abierta con urllib.urlopen, se puede
tratar como un archivo y leer a través de ella usando un bucle for.
import urllib
contadores = dict()
manf =
urllib.urlopen('http://www.py4inf.com/code/romeo.txt')
for linea in manf:
palabras = linea.split()
for palabra in palabras:
contadores[palabra] = contadores.get(palabra,0)
+ 1
print contadores
Vemos de nuevo que una vez abierta la página web se puede leer como si
fuera un fichero local.
Google utiliza también la frecuencia con que las páginas que encuentra
enlazan hacia una página concreta para calcular la “importancia” de esa
página, y la posición en la que debe aparecer dentro de sus resultados de
búsqueda.
Podemos construir una expresión regular bien formada que busque y extraiga
los valores de los enlaces del texto anterior, de éste modo:
href="http://.+?"
import urllib
import re
python urlregex.py
Introduce - http://www.dr-chuck.com/page1.htm
http://www.dr-chuck.com/page2.htm
python urlregex.py
Introduce - http://www.py4inf.com/book.htm
http://www.greenteapress.com/thinkpython/thinkpython.ht
ml
http://allendowney.com/
http://www.py4inf.com/code
http://www.lib.umich.edu/espresso-book-machine
http://www.py4inf.com/py4inf-slides.zip
Las expresiones regulares funcionan muy bien cuando el HTML está bien
formado y es predecible. Pero dado que ahí fuera hay muchas páginas con
HTML “defectuoso”, una solución usando solamente expresiones regulares
puede o bien perder parte de los enlaces correctos, o bien terminar
obteniendo datos erróneos.
http://www.crummy.com/software/
Se puede descargar e “instalar” BeautifulSoup, o simplemente colocar el
archivo BeautifulSoup.py en la misma carpeta que nuestra aplicación.
import urllib
from BeautifulSoup import *
El programa solicita una dirección web, luego abre la página web, lee los
datos y se los pasa al analizador BeautifulSoup, que recupera todas las
etiquetas de anclaje e imprime en pantalla el atributo href de cada una de
ellas.
python urllinks.py
Introduce - http://www.dr-chuck.com/page1.htm
http://www.dr-chuck.com/page2.htm
python urllinks.py
Introduce - http://www.py4inf.com/book.htm
http://www.greenteapress.com/thinkpython/thinkpython.ht
ml
http://allendowney.com/
http://www.si502.com/
http://www.lib.umich.edu/espresso-book-machine
http://www.py4inf.com/code
http://www.pythonlearn.com/
import urllib
from BeautifulSoup import *
python urllink2.py
Introduce - http://www.dr-chuck.com/page1.htm
ETIQUETA: <a href="http://www.dr-chuck.com/page2.htm">
Second Page</a>
URL: http://www.dr-chuck.com/page2.htm
Contenido: [u'\nSecond Page']
Atributos: [(u'href', u'http://www.dr-
chuck.com/page2.htm')]
Estos ejemplos tan sólo insinúan la potencia de BeautifulSoup en el análisis
del HTML. Lee la documentación y los ejemplos que están en
http://www.crummy.com/software/BeautifulSoup/ para obtener más
detalles.
img =
urllib.urlopen('http://www.py4inf.com/cover.jpg').read(
)
manf = open('portada.jpg', 'w')
manf.write(img)
manf.close()
Este programa lee todos los datos de una sola vez a través de la red y los
almacena en la variable img en la memoria principal de tu PC, luego abre el
fichero portada.jpg y escribe los datos en el disco. Esto funcionará sólo si
el tamaño del fichero es menor que el tamaño de la memoria de tu equipo.
img = urllib.urlopen('http://www.py4inf.com/cover.jpg')
manf = open('portada.jpg', 'w')
tamano = 0
while True:
info = img.read(100000)
if len(info) < 1 : break
tamano = tamano + len(info)
manf.write(info)
python curl2.py
568248 caracteres copiados.
curl -O http://www.py4inf.com/cover.jpg
BeautifulSoup:
Una librería Python para analizar documentos HTML y extaer datos de
ellos que compensa la mayoría de las imperfecciones que los
navegadores HTML normalmente ignoran. Puedes descargar el código
de BeautifulSoup desde www.crummy.com.
puerto:
Un número que generalmente indica con qué aplicación estás
contactando cuando realizas una conexión con un socket en un servidor.
Por ejemplo, el tráfico web normalmente usa el puerto 80, mientras que
el tráfico del correo electrónico usa el puerto 25.
rastrear:
La acción de un motor de búsqueda web de recuperar una página y luego
todas las páginas enlazadas por ella, continuando así sucesivamente
hasta que tiene casi todas las páginas de Internet, que usan para construir
su índice de búsqueda.
socket:
Una conexión de red entre dos aplicaciones, en la cual dichas
aplicaciones pueden enviar y recibir datos en ambas direcciones.
scraping (rascado):
Cuando un programa simula ser un navegador web y recupera una
página web, y luego realiza una búsqueda en su contenido. A menudo los
programas siguen los enlaces en una página para encontrar la siguiente,
de modo que pueden atravesar una red de páginas o una red social.
12.10 Ejercicios
1
El formato XML será descrito en el próximo capítulo.
Chapítulo 13 Uso de servicios web
XML tiene un aspecto muy parecido a HTML, pero XML está más
estructurado. Esto es un ejemplo de un documento XML:
<persona>
<nombre>Chuck</nombre>
<telefono tipo="intl">
+1 734 303 4456
</telefono>
<email oculto="si"/>
</persona>
He aquí una aplicación sencilla que analiza el XML anterior y extrae algunos
elementos de él:
import xml.etree.ElementTree as ET
datos = "'
<persona>
<nombre>Chuck</nombre>
<telefono tipo="intl">
+1 734 303 4456
</telefono>
<email oculto="si"/>
</persona>"'
arbol = ET.fromstring(datos)
print 'Nombre:',arbol.find('nombre').text
print 'Attr:',arbol.find('email').get('oculto')
La función find busca a través del árbol XML y recupera un nodo que
coincide con la etiqueta especificada. Cada nodo tiene cierto texto, ciertos
atributos (como en este caso “oculto”), y ciertos nodos “hijos”. Cada nodo
puede ser el origen de otro árbol de nodos.
Nombre: Chuck
Attr: si
import xml.etree.ElementTree as ET
entrada = "'
<cosa>
<usuarios>
<usuario x="2">
<id>001</id>
<nombre>Chuck</nombre>
</usuario>
<usuario x="7">
<id>009</id>
<nombre>Brent</nombre>
</usuario>
</usuarios>
</cosa>"'
cosa = ET.fromstring(entrada)
lst = cosa.findall('usuarios/usuario')
print 'Cantidad de usuarios:', len(lst)
Cantidad de usuarios: 2
Nombre Chuck
Id 001
Atributo 2
Nombre Brent
Id 009
Atributo 7
He aquí una codificación JSON que es más o menos equivalente al XML del
ejemplo anterior:
{
"nombre" : "Chuck",
"telefono" : {
"tipo" : "intl",
"numero" : "+1 734 303 4456"
},
"email" : {
"oculto" : "si"
}
}
En general, las estructuras JSON son más simples que las de XML, debido a
que JSON tiene menos capacidades. Pero JSON tiene la ventaja de que
mapea directamente hacia una combinación de diccionarios y listas. Y dado
que casi todos los lenguajes de programación tienen algo equivalente a los
diccionarios y listas de Python, JSON es un formato muy intuitivo para que
dos programas que vayan a cooperar intercambien datos.
import json
entrada = "'
[
{ "id" : "001",
"x" : "2",
"nombre" : "Chuck"
} ,
{ "id" : "009",
"x" : "7",
"nombre" : "Brent"
}
]"'
info = json.loads(entrada)
print 'Cantidad de usuarios:', len(info)
Si comparas el código que extrae los datos del JSON y XML analizado,
verás que lo que obtenemos de json.loads() es una lista de Python que
recorreremos con un bucle for, y cada elemento dentro de esa lista es un
diccionario de Python. Una vez analizado el JSON, podemos usar el
operador índice de Python para extraer los distintos fragmentos de datos de
cada usuario. No tenemos que usar la librería JSON para rebuscar a través
del JSON analizado, ya que los datos devueltos son simplemente estructuras
nativas de Python.
Cantidad de usuarios: 2
Nombre Chuck
Id 001
Atributo 2
Nombre Brent
Id 009
Atributo 7
import urllib
import json
urlservicio =
'http://maps.googleapis.com/maps/api/geocode/json?'
while True:
direccion = raw_input('Introduce ubicación: ')
if len(direccion) < 1 : break
url = urlservicio +
urllib.urlencode({'sensor':'false',
'address': direccion})
print 'Recuperando', url
uh = urllib.urlopen(url)
datos = uh.read()
print 'Recibidos',len(datos),'caracteres'
try: js = json.loads(str(datos))
except: js = None
if 'status' not in js or js['status'] != 'OK':
print '==== Fallo de Recuperación ===='
print datos
continue
lat = js["results"][0]["geometry"]["location"]
["lat"]
lng = js["results"][0]["geometry"]["location"]
["lng"]
print 'lat',lat,'lng',lng
ubicacion = js['results'][0]['formatted_address']
print ubicacion
Una vez recuperados los datos JSON, los analizamos con la librería json y
realizamos unas pequeñas comprobaciones para asegurarnos de que hemos
recibido datos válidos. Finalmente, extraemos la información que estábamos
buscando.
$ python geojson.py
Introduce ubicación: Ann Arbor, MI
Recuperando http://maps.googleapis.com/maps/api/
geocode/json?sensor=false&address=Ann+Arbor%2C+MI
Recibidos 1669 caracteres
{
"status": "OK",
"results": [
{
"geometry": {
"location_type": "APPROXIMATE",
"location": {
"lat": 42.2808256,
"lng": -83.7430378
}
},
"address_components": [
{
"long_name": "Ann Arbor",
"types": [
"locality",
"political"
],
"short_name": "Ann Arbor"
}
],
"formatted_address": "Ann Arbor, MI, USA",
"types": [
"locality",
"political"
]
}
]
}
lat 42.2808256 lng -83.7430378
Ann Arbor, MI, USA
Introduce ubicación:
Resulta bastante frecuente que se necesite algún tipo de “clave API” para
hacer uso de una API comercial. La idea general es que ellos quieren saber
quién está usando sus servicios y cuánto los utiliza cada usuario. Tal vez
tienen distintos niveles (gratuitos y de pago) de sus servicios, o una política
que limita el número de peticiones que un único usuario puede realizar
durante un determinado periodo de tiempo.
A veces, una vez que tienes tu clave API, tan sólo debes incluirla como parte
de los datos POST, o tal vez como parámetro dentro de la URL que usas para
llamar a la API.
Para usar estos programas debes tener una cuenta de Twitter, y autorizar a tu
código Python como aplicación permitida, estableciendo diversos
parámetros (key, secret, token y token secret). Debes editar el archivo
hidden.py y colocar esas cuatro cadenas en las variables apropiadas dentro
del fichero:
def auth() :
return { "consumer_key" : "h7L...GNg",
"consumer_secret" : "dNK...7Q",
"token_key" : "101...GI",
"token_secret" : "H0yM...Bo" }
Se puede acceder al servicio web de Twitter mediante una URL como ésta:
https://api.twitter.com/1.1/statuses/user_timeline.json
https://api.twitter.com/1.1/statuses/user_timeline.json
?count=2
&oauth_version=1.0&oauth_token=101...SGI&screen_name=dr
chuck
&oauth_nonce=09239679&oauth_timestamp=1380395644
&oauth_signature=rLK...BoD&oauth_consumer_key=h7Lu...GN
g
&oauth_signature_method=HMAC-SHA1
import urllib
import twurl
TWITTER_URL='https://api.twitter.com/1.1/statuses/user_
timeline.json'
while True:
print "
cuenta = raw_input('Introduce Cuenta de Twitter:')
if ( len(cuenta) < 1 ) : break
url = twurl.augment(TWITTER_URL,
{'screen_name': cuenta, 'count': '2'} )
print 'Recuperando', url
conexion = urllib.urlopen(url)
datos = conexion.read()
print datos[:250]
cabeceras = conexion.info().dict
# print cabeceras
print 'Restante', cabeceras['x-rate-limit-
remaining']
Junto con los datos de la línea del tiempo, Twitter también devuelve
metadatos sobre la petición en las cabeceras de respuesta HTTP. Una
cabecera en particular, x-rate-limit-remaining, nos informa sobre cuántas
peticiones podemos hacer antes de que seamos bloqueados por un corto
periodo de tiempo. Puedes ver cómo cada vez que realizamos una petición a
la API nuestros intentos restantes van disminuyendo.
import urllib
import twurl
import json
TWITTER_URL =
'https://api.twitter.com/1.1/friends/list.json'
while True:
print "
cuenta = raw_input('Introduce Cuenta de Twitter:')
if ( len(cuenta) < 1 ) : break
url = twurl.augment(TWITTER_URL,
{'screen_name': cuenta, 'count': '5'} )
print 'Recuperando', url
conexion = urllib.urlopen(url)
datos = conexion.read()
cabeceras = conexion.info().dict
print 'Restantes', cabeceras['x-rate-limit-
remaining']
js = json.loads(datos)
print json.dumps(js, indent=4)
for u in js['users'] :
print u['screen_name']
s = u['status']['text']
print ' ',s[:50]
El último trozo de la salida es donde podemos ver cómo el bucle for lee los
cinco “amigos” más nuevos de la cuenta de Twitter del drchuck e imprime
el estado más reciente de cada uno de ellos. Hay muchos más datos
disponibles en el JSON devuelto. Si miras la salida del programa, podrás
ver que el “encuentra a los amigos” de una cuenta particular tiene una
limitación de usos distinta al número de consultas de líneas del tiempo que
está permitido realizar en un periodo de tiempo.
13.9 Glosario
API:
Interfaz de Programación de Aplicaciones - Un contrato entre
aplicaciones que define las pautas de interacción entre los componentes
de dos aplicaciones.
ElementTree:
Una librería interna de Python que se utiliza para analizar datos XML.
JSON:
Notación de Objetos JavaScript. Un formato que permite el envío de
estructuras de datos basadas en la sintaxis de los Objetos JavaScript.
SOA:
Arquitectura Orientada a Servicios. Cuando una aplicación está formada
por componentes conectados a través de una red.
XML:
Lenguaje de Marcas eXtensible. Un formato que permite el envío de
datos estructurados.
13.10 Ejercicios
Una base de datos es un archivo que está organizado para almacenar datos.
La mayoría de las bases de datos están organizadas como diccionarios, en el
sentido de que realizan mapeados entre claves y valores. La diferencia más
importante es que la base de datos se encuentra en el disco (u otro
almacenamiento permanente), de modo que su contenido se conserva después
de que el programa finaliza. Gracias a que la base de datos se guarda en un
almacenamiento permanente, puede almacenar muchos más datos que un
diccionario, que está limitado al tamaño de la memoria que tenga el equipo.
https://addons.mozilla.org/en-us/firefox/addon/sqlite-
manager/
Las bases de datos necesitan una estructura más definida que las listas o
diccionarios de Python1.
Cuando se crea una tabla, se debe indicar de antemano a la base de datos los
nombres de cada una de las columnas de la tabla y el tipo de datos que se
van a almacenar en cada columna. Cuando el software de la base de datos
conoce el tipo de datos de cada columna, puede elegir el modo más eficiente
de almacenar y buscar en ellas, de acuerdo al tipo de datos guardados.
El tener que definir de antemano una estructura para los datos puede parecer
incómodo al principio, pero la recompensa consiste en obtener un acceso
rápido a los datos, incluso cuando la base de datos contiene una gran
cantidad de ellos.
import sqlite3
conn = sqlite3.connect('musica.sqlite3')
cur = conn.cursor()
conn.close()
La operación connect realiza una “conexión” con la base de datos
almacenada en el archivo musica.sqlite3 del directorio actual. Si el
archivo no existe, se creará nuevo. La razón de que se le llame una
“conexión” es que a veces la base de datos se almacena en un “servidor de
bases de datos”, distinto del servidor en el cual está funcionando nuestra
aplicación. En nuestros ejemplos, dado que son sencillos, la base de datos
será simplemente un archivo local en el mismo directorio en el que está
funcionando el código de Python.
import sqlite3
conn = sqlite3.connect('musica.sqlite3')
cur = conn.cursor()
print 'Canciones:'
cur.execute('SELECT titulo, reproducciones FROM
Canciones')
for fila in cur :
print fila
cur.close()
Después usamos el comando SELECT para recuperar las filas que acabamos
de insertar en la tabla. En el comando SELECT, indicamos qué columnas nos
gustaría obtener (titulo, reproducciones) e indicamos desde qué tabla
queremos recuperar los datos. Después de ejecutar la sentencia SELECT, el
cursor se convierte en algo con lo que podemos iterar mediante una sentencia
for. Por eficiencia, el cursor no lee todos los datos de la base de datos
cuando se ejecuta la sentencia SELECT. En lugar de ello, los datos van siendo
leídos según se van pidiendo las filas desde el bucle creado con la sentencia
for.
Nuestro bucle for encuentra dos filas, y cada fila es una tupla de Python
cuyo primer valor es el título y el segundo es el número de
reproducciones. No nos preocupa que la cadena del título comience por
u’. Se trata de una indicación de que las cadenas son del tipo Unicode, y por
tanto capaces de almacenar conjuntos de caracteres no-latinos.
Una base de datos relacional está compuesta por tablas, filas y columnas.
Las columnas tienen generalmente un tipo de datos que puede ser texto,
numérico, o fecha. Cuando se crea una tabla, se indican los nombres y tipos
de cada columna.
Para insertar una fila en una tabla, usamos el comando de SQL INSERT:
El * indica que se desea que la base de datos devuelva todas las columnas
para cada línea que cumpla la condición de la clausula WHERE.
Se puede solicitar que las columnas devueltas vengan ordenadas por uno de
los campos así:
SELECT titulo, reproducciones FROM Canciones ORDER BY
titulo
Para eliminar una fila, es necesario usar una clausula WHERE en una sentencia
DELETE de SQL. La clausula WHERE determina qué filas serán eliminadas:
import urllib
import twurl
import json
import sqlite3
TWITTER_URL =
'https://api.twitter.com/1.1/friends/list.json'
conn = sqlite3.connect('arana.sqlite3')
cur = conn.cursor()
cur.execute("'
CREATE TABLE IF NOT EXISTS Twitter
(nombre TEXT, recuperado INTEGER, amigos INTEGER)"')
while True:
cuenta = raw_input('Introduce una cuenta de Twitter
o salir: ')
if ( cuenta == 'salir' ) : break
if ( len(cuenta) < 1 ) :
cur.execute('SELECT nombre FROM Twitter WHERE
recuperado = 0 LIMIT 1')
try:
cuenta = cur.fetchone()[0]
except:
print 'No se han encontrado cuentas de
Twitter por recuperar'
continue
url = twurl.augment(TWITTER_URL,
{'screen_name': cuenta, 'count': '20'} )
print 'Recuperando', url
conexion = urllib.urlopen(url)
datos = conexion.read()
cabeceras = conexion.info().dict
# print 'Restante', cabeceras['x-rate-limit-
remaining']
js = json.loads(data)
# print json.dumps(js, indent=4)
contnuevas = 0
contantiguas = 0
for u in js['users'] :
amigo = u['screen_name']
print amigo
cur.execute('SELECT amigos FROM Twitter WHERE
nombre = ? LIMIT 1',
(amigo, ) )
try:
contador = cur.fetchone()[0]
cur.execute('UPDATE Twitter SET amigos = ?
WHERE nombre = ?',
(contador+1, amigo) )
contantiguas = contantiguas + 1
except:
cur.execute("'INSERT INTO Twitter (nombre,
recuperado, amigos)
VALUES ( ?, 0, 1 )"', ( amigo, ) )
contnuevas = contnuevas + 1
print 'Cuentas nuevas=',contnuevas,' ya
visitadas=',contantiguas
conn.commit()
cur.close()
contnuevas = 0
contantiguas = 0
for u in js['users'] :
amigo = u['screen_name']
print amigo
cur.execute('SELECT amigos FROM Twitter WHERE
nombre = ? LIMIT 1',
(amigo, ) )
try:
contador = cur.fetchone()[0]
cur.execute('UPDATE Twitter SET amigos = ? WHERE
nombre = ?',
(contador+1, amigo) )
contantiguas = contantiguas + 1
except:
cur.execute("'INSERT INTO Twitter (nombre,
recuperado, amigos)
VALUES ( ?, 0, 1 )"', ( amigo, ) )
contnuevas = contnuevas + 1
print 'Cuentas nuevas=',contnuevas,' ya
visitadas=',contantiguas
conn.commit()
Una vez que el cursor ejecuta la sentencia SELECT, debemos recuperar las
filas. Podríamos hacerlo con una sentencia for, pero dado que sólo estamos
recuperando una única fila (LIMIT 1), podemos también usar el método
fetchone() para extraer la primera (y única) fila que da como resultado la
operación SELECT. Dado que fetchone() devuelve la fila como una tupla
(incluso si sólo contiene un campo), tomamos el primer valor de la tupla
mediante [0], para almacenar así dentro de la variable contador el valor
del contador de amigos actual.
Si esta operación tiene éxito, usamos la sentencia UPDATE de SQL con una
clausula WHERE para añadir 1 a la columna amigos de aquella fila que
coincida con la cuenta del amigo. Fíjate que hay dos marcadores de posición
(es decir, signos de interrogación) en el SQL, y que el segundo parámetro de
execute() es una tupla de dos elementos que contiene los valores que serán
sustituidos por esas interrogaciones dentro de la sentencia SQL.
import sqlite3
conn = sqlite3.connect('arana.sqlite3')
cur = conn.cursor()
cur.execute('SELECT * FROM Twitter')
contador = 0
for fila in cur :
print fila
contador = contador + 1
print contador, 'filas.'
cur.close()
(u'opencontent', 0, 1)
(u'lhawthorn', 0, 1)
(u'steve_coppin', 0, 1)
(u'davidkocher', 0, 1)
(u'hrheingold', 0, 1)
...
20 filas.
Vemos una fila para cada nombre, que aún no hemos recuperado los datos de
ninguno de esos nombres, y que todo el mundo en la base de datos tiene un
amigo.
Como hemos pulsado intro (es decir, no hemos especificado otra cuenta de
Twitter), se ha ejecutado el código siguiente:
if ( len(cuenta) < 1 ) :
cur.execute('SELECT nombre FROM Twitter WHERE
recuperado = 0 LIMIT 1')
try:
cuenta = cur.fetchone()[0]
except:
print 'No se han encontrado cuentas de
Twitter por recuperar'
continue
Si hemos recuperado con éxito el nombre de una cuenta que aún no había
sido procesada, tratamos sus datos de este modo:
(u'opencontent', 1, 1)
(u'lhawthorn', 1, 1)
(u'steve_coppin', 0, 1)
(u'davidkocher', 0, 1)
(u'hrheingold', 0, 1)
...
(u'cnxorg', 0, 2)
(u'knoop', 0, 1)
(u'kthanos', 0, 2)
(u'LectureTools', 0, 1)
...
55 rows.
Podemos ver que se han guardado correctamente las visitas que hemos
realizado a a lhawthorn y opencontent. Además las cuentas cnxorg y
kthanos ya tienen dos seguidores. A pesar de hasta ahora hemos
recuperados sólo los amigos de tres personas (drchuck, opencontent, y
lhawthorn), la tabla contiene ya 55 filas de amigos por recuperar.
Como ves, al estar los datos del programa almacenados en el disco en una
base de datos, la actividad de rastreo puede ser suspendida y reanudada
tantas veces como se desee sin que se produzca ninguna pérdida de datos.
14.7 Modelado de datos básico
Cada vez que encontremos a una persona de las que está siguiendo drchuck,
insertaremos una fila de esta forma:
Esta duplicación de cadenas de datos viola una de las mejores prácticas para
la normalización de bases de datos, que básicamente consiste en que nunca
se debe guardar la misma cadena de datos más de una vez en la base de
datos. Si se necesitan los datos varias veces, se debe crear una clave
numérica para ellos y hacer referencia a los datos reales a través de esa
clave.
Podemos, pues, crear la tabla Personas con esa columna adicional id, como
se muestra a continuación:
import urllib
import twurl
import json
import sqlite3
TWITTER_URL =
'https://api.twitter.com/1.1/friends/list.json'
conn = sqlite3.connect('amigos.sqlitesqlite3')
cur = conn.cursor()
while True:
cuenta = raw_input('Introduce una cuenta de
Twitter, o salir: ')
if ( cuenta == 'salir' ) : break
if ( len(cuenta) < 1 ) :
cur.execute("'SELECT id, nombre FROM Personas
WHERE recuperado = 0 LIMIT 1"')
try:
(id, cuenta) = cur.fetchone()
except:
print 'No se han encontrado cuentas de
Twitter sin recuperar'
continue
else:
cur.execute('SELECT id FROM Personas WHERE
nombre = ? LIMIT 1',
(cuenta, ) )
try:
id = cur.fetchone()[0]
except:
cur.execute("'INSERT OR IGNORE INTO
Personas (nombre, recuperado)
VALUES ( ?, 0)"', ( cuenta, ) )
conn.commit()
if cur.rowcount != 1 :
print 'Error insertando cuenta:',cuenta
continue
id = cur.lastrowid
url = twurl.augment(TWITTER_URL,
{'screen_name': cuenta, 'count': '20'} )
print 'Recuperando cuenta', cuenta
conexion = urllib.urlopen(url)
datos = conexion.read()
cabeceras = conexion.info().dict
print 'Restantes', cabeceras['x-rate-limit-
remaining']
js = json.loads(datos)
# print json.dumps(js, indent=4)
cur.close()
Dado que con el tiempo es probable que vayan aumentando las posibilidades
de que la cuenta ya figure en la base de datos, primero comprobamos si el
registro existe en Personas, usando una sentencia SELECT.
amigo = u['screen_name']
cur.execute('SELECT id FROM Personas WHERE
nombre = ? LIMIT 1',
(amigo, ) )
try:
amigo_id = cur.fetchone()[0]
contantiguas = contantiguas + 1
except:
cur.execute("'INSERT OR IGNORE INTO
Personas (nombre, recuperado)
VALUES ( ?, 0)"', ( amigo, ) )
conn.commit()
if cur.rowcount != 1 :
print 'Error al insertar cuenta:',amigo
continue
amigo_id = cur.lastrowid
contnuevas = contnuevas + 1
Fíjate que dejamos que la base de datos se ocupe por nosotros de evitar la
“doble-inserción” de una relación, mediante la creación de una tabla con una
restricción de unicidad, de modo que luego tan sólo añadimos o ignoramos
en nuestra sentencia INSERT.
Personas:
(1, u'drchuck', 1)
(2, u'opencontent', 1)
(3, u'lhawthorn', 1)
(4, u'steve_coppin', 0)
(5, u'davidkocher', 0)
55 filas.
Seguimientos:
(1, 2)
(1, 3)
(1, 4)
(1, 5)
(1, 6)
60 filas.
Una clave lógica es una clave que se podría usar en el “mundo real”
para localizar una fila. En nuestro ejemplo de modelado de datos, el
campo nombre es una clave lógica. Es el nombre que se muestra en
pantalla para el usuario y, en efecto, buscamos la columna de un usuario
varias veces en el programa usando el campo nombre. A menudo verás
que tiene sentido añadir una restricción UNIQUE (única) a una clave
lógica. Ya que las claves lógicas son las que usamos para buscar una
fila desde el mundo exterior, tendría poco sentido permitir que hubiera
múltiples filas con el mismo valor en la tabla.
Una clave primaria es normalmente un número que es asignado
automáticamente por la base de datos. En general no tiene ningún
significado fuera del programa y sólo se utiliza para enlazar entre si
filas de tablas diferentes. Cuando queramos buscar una fila en una tabla,
realizar la búsqueda usando la clave primaria es, normalmente, el modo
más rápido de localizarla. Como las claves primarias son números
enteros, necesitan muy poco espacio de almacenamiento y pueden ser
comparadas u ordenadas muy rápido. En nuestro modelo de datos, el
campo id es un ejemplo de una clave primaria.
Una clave foránea3 es normalmente un número que apunta a la clave
primaria de una fila asociada en una tabla diferente. Un ejemplo de una
clave foránea en nuestro modelo de datos es la columna desde_id.
SQL usa la clausula JOIN para volver a conectar esas tablas. En la clausula
JOIN se especifican los campos que se utilizan para reconectar las filas entre
las distintas tablas.
La clausula JOIN indica que los campos que estamos seleccionando cruzan
las tablas Seguimientos y Personas. La clausula ON indica cómo deben ser
unidas las dos tablas: Toma cada fila de Seguimientos y añade una fila de
Personas en la cual el campo desde_id de Seguimientos coincide con el
valor id en la tabla Personas.
import sqlite3
conn = sqlite3.connect('arana.sqlite3')
cur = conn.cursor()
cur.execute('SELECT * FROM Personas')
contador = 0
print 'Personas:'
for fila in cur :
if contador < 5: print fila
contador = contador + 1
print contador, 'filas.'
cur.close()
python twjoin.py
Personas:
(1, u'drchuck', 1)
(2, u'opencontent', 1)
(3, u'lhawthorn', 1)
(4, u'steve_coppin', 0)
(5, u'davidkocher', 0)
55 filas.
Seguimientos:
(1, 2)
(1, 3)
(1, 4)
(1, 5)
(1, 6)
60 filas.
Conexiones para id=2:
(2, 1, 1, u'drchuck', 1)
(2, 28, 28, u'cnxorg', 0)
(2, 30, 30, u'kthanos', 0)
(2, 102, 102, u'SomethingGirl', 0)
(2, 103, 103, u'ja_Pac', 0)
20 filas.
En cada una de las “meta-filas” del último select, las primeras dos columnas
pertenecen a la tabla Seguimientos, mientras que las columnas tres a cinco
pertenecen a la tabla Personas. Se puede observar también cómo la segunda
columna (Seguimientos.hacia_id) coincide con la tercera (Personas.id)
en cada una de las “meta-filas” del join.
14.11 Resumen
En este capítulo se han tratado un montón de temas para darte una visión de
conjunto del uso básico de las bases de datos en Python. Es más complicado
escribir el código para usar una base de datos que almacene los datos que
utilizar diccionarios de Python o archivos planos, de modo que existen pocas
razones para usar una base de datos a menos que tu aplicación necesite de
verdad las capacidades que proporciona. Las situaciones en las cuales una
base de datos pueden resultar bastante útil son: (1) cuando tu aplicación
necesita realizar muchos cambios pequeños de forma aleatoria en un
conjunto de datos grandes, (2) cuando tienes tantos datos que no caben en un
diccionario y necesitas localizar información con frecuencia, o (3) cuando
tienes un proceso que va a funcionar durante mucho tiempo, y necesitas
poder detenerlo y volverlo a poner en marcha, conservando los datos entre
ejecuciones.
Para cubrir las necesidades de muchas aplicaciones, una base de datos con
una simple tabla puede resultar suficiente, pero la mayoría de los problemas
necesitarán varias tablas y enlaces/relaciones entre filas de tablas diferentes.
Cuando empieces a crear enlaces entre tablas, es importante realizar un
diseño meditado y seguir las reglas de normalización de bases de datos para
conseguir el mejor uso de sus capacidades. Como la motivación principal
para usar una base de datos suele ser tener grandes cantidades de datos con
las que tratar, resulta importante modelar los datos de forma eficiente, de
modo que tu programa funcione tan rápidamente como sea posible.
14.12 Depuración
Debes tener cuidado, ya que SQLite se encarga de evitar que dos programa
puedan cambiar los mismos datos a la vez. Por ejemplo, si abres una base de
datos en el navegador y realizas un cambio en la base de datos, pero no has
pulsado aún el botón “guardar” del navegador, éste “bloqueará” el fichero
de la base de datos y evitará que cualquier otro programa acceda a dicho
fichero. Concretamente, en ese caso tu programa Python no será capaz de
acceder al fichero si éste se encuentra bloqueado.
De modo que la solución pasa por asegurarse de cerrar el navegador de la
base de datos, o bien usar el menú Arhivo para cerrar la base de datos
abierta en el navegador antes de intentar acceder a ella desde Python, para
evitar encontrarse con el problema de que el código de Python falle debido a
que la base de datos está bloqueada.
14.13 Glosario
atributo:
Uno de los valores dentro de una tupla. Más comúnmente llamada
“columna” o “campo”..
cursor:
Un cursor permite ejecutar comandos SQL en una base de datos y
recuperar los datos de ella. Un cursor es similar a un socket en
conexiones de red o a un manejador de ficheros.
clave foránea:
Una clave numérica que apunta a la clave primaria de una fila en otra
tabla. Las claves foráneas establecen relaciones entre filas almacenadas
en tablas diferentes.
clave lógica:
Una clave que el “mundo exterior” utiliza para localizar una fila
concreta. Por ejemplo, en una tabla de cuentas de usuario, la dirección
de e-mail de una persona sería un buen candidato a utilizar como clave
lógica para los datos de ese usuario.
clave primaria:
Una clave numérica asignada a cada fila que es utilizada para referirnos
a esa fila concreta de esa tabla desde otra tabla distinta. A menudo la
base de datos se configura para asignar las claves primarias de forma
automática, según se van insertando filas.
índice:
Datos adicionales que el software de la base de datos mantiene como
filas e inserta en una tabla para conseguir que las búsquedas sean muy
rápidas.
navegador de base de datos:
Un programa que permite conectar directamente con una base de datos y
manipularla, sin tener que escribir código para ello.
normalización:
Diseño de un modelado de datos de forma que no haya datos
duplicados. Se almacena cada elemento de los datos en un lugar
concreto de la base de datos y se referencia desde otros sitios usando
una clave foránea.
relación:
Un área dentro de una base de datos que contiene tuplas y atributos. Se
la conoce más habitualmente como “tabla”.
restricción:
Cuando le pedimos a una base de datos que imponga una regla a una
campo de una fila en una tabla. Una restricción habitual consiste en
especificar que no pueda haber valores repetidos en un campo concreto
(es decir, que todos los valores deban ser únicos).
tupla:
Una entrada única en una base de datos, que es un conjunto de atributos.
Se la conoce más habitualmente como “fila”.
1
SQLite en realidad permite cierta flexibilidad respecto al tipo de datos
que se almacenan en cada columna, pero en este capítulo nosotros
vamos a mantener los tipos de datos estrictos para que los conceptos
que aprendamos puedan ser igualmente aplicados a otras bases de datos
como MySQL.
2
En general, cuando una frase comienza “si todo va bien” es porque el
código del que se habla necesita utilizar try/except.
3
Se trata de una “foreign key”, la cual se traduce también a veces al
español como “clave extranjera” (Nota del trad.)
Chapítulo 15 Visualización de datos
www.py4inf.com/code/geodata.zip
El primer problema a resolver es que la API libre de geocodificación de
Google tiene como límite de uso un cierto número de peticiones diarias. Si
tienes un montón de datos, necesitarás detener y reanudar el proceso de
búsqueda varias veces. De modo que dividiremos el problema en dos fases.
Las primeras cinco ubicaciones ya están en la base de datos y por eso las
omitimos. El programa explora hasta que encuentra ubicaciones nuevas y
entonces comienza a recuperarlas.
Una vez que tienes parte de los datos cargados en geodata.sqlite, se pueden
visualizar usando el programa geodump.py. Este programa lee la base de
datos y escribe el arhivo where.js con la ubicación, latitud y longitud en
forma de código ejecutable JavaScript.
myData = [
[42.3396998,-71.08975, 'Northeastern Uni ... Boston, MA
02115'],
[40.6963857,-89.6160811, 'Bradley University, ...
Peoria, IL 61625, USA'],
[32.7775,35.0216667, 'Technion, Viazman 87, Kesalsaba,
32000, Israel'],
...
];
Una vez que tienes unas cuantas páginas en la base de datos, puedes ejecutar
el clasificador sobre ellas, usando el programa sprank.py. Simplemente
debes indicarle cuántas iteraciones del clasificador de páginas debe realizar.
Si has llegado hasta este punto del libro, ya debes de estar bastante
familiarizado con nuestros ficheros de datos mbox-short.txt y mbox.txt.
Ahora es el momento de llevar nuestro análisis de datos de correo
electrónico al siguiente nivel.
En el mundo real, a veces se tienen que descargar datos de correo desde los
servidores. Eso podría llevar bastante tiempo y los datos podrían tener
inconsistencias, estar llenos de errores, y necesitar un montón de limpieza y
ajustes. En esta sección, trabajaremos con la aplicación más compleja que
hemos visto hasta ahora, que descarga casi un gigabyte de datos y los
visualiza.
www.py4inf.com/code/gmane.zip
http://gmane.org/export.php
Cuando se usa este software para rastrear los datos de correo de Sakai, se
genera casi un Gigabyte de datos y se necesita una cantidad considerable de
ejecuciones durante varios días. El archivo README.txt del ZIP anterior
contiene instrucciones sobre cómo descargar una copia pre-rastreada del
fichero content.sqlite con la mayor parte del contenido de los correos de
Sakai, de modo que no tengas que rastrear durante cinco días sólo para hacer
funcionar los programas. Aunque descargues el contenido pre-rastreado,
deberías ejecutar el proceso de rastreo para recuperar los mensajes más
recientes.
El fichero gmane.py opera como una araña caché responsable, que funciona
despacio y recupera un mensaje de correo por segundo para evitar ser
bloqueado por gmane. Almacena todos sus datos en una base de datos y
puede ser interrumpido y reanudado tantas veces como sean necesarias.
Puede llevar muchas horas descargar todos los datos. De modo que tendrás
que reanudarlo varias veces.
Los nombres de dominio son truncados a dos niveles para .com, .org, .edu y
.net. Otros nombres de dominio son truncados a tres niveles. De modo que
si.umich.edu se transforma en umich.edu, y caret.cam.ac.uk queda como
cam.ac.uk. Las direcciones de correo electrónico también son transformadas
a minúsculas, y algunas de las direcciones de @gmane.org, como las
siguientes
[email protected]
[email protected]
[email protected]
Puedes volver a ejecutar gmodel.py una y otra vez mientras vas mirando los
datos, y añadir mapeos para hacer que los datos queden más y más limpios.
Cuando lo hayas hecho, tendrás una bonita versión indexada del correo en
index.sqlite. Éste es el fichero que usaremos para realizar el análisis de
datos. Con ese fichero, el análisis de datos se realizará muy rápidamente.
Fijate cómo gbasic.py funciona mucho más rápido que gmane.py, e incluso
que gmodel.py. Todos trabajan con los mismos datos, pero gbasic.py está
usando los datos comprimidos y normalizados de index.sqlite. Si tienes un
montón de datos que gestionar, un proceso multipaso como el que se realiza
en esta aplicación puede ser más largo de desarrollar, pero te ahorrará un
montón de tiempo cuando realmente comiences a explorar y visualizar los
datos.
Puedes generar una vista sencilla con la frecuencia de cada palabras en las
líneas de título, usando el archivo gword.py:
>>> import os
>>> cwd = os.getcwd()
>>> print cwd
/Users/csev
cwd significa current working directory (directorio de trabajo actual). El
resultado en este ejemplo es /Users/csev, que es el directorio de partida
(home) para un usuario llamado csev.
Una cadena como cwd, que identifica un fichero, recibe el nombre de ruta.
Una ruta relativa comienza en el directorio actual; una ruta absoluta
comienza en el directorio superior del sistema de archivos.
Las rutas que hemos visto hasta ahora son simples nombres de archivo, de
modo que son relativas al directorio actual. Para encontrar la ruta absoluta
de un archivo se puede utilizar os.path.abspath:
>>> os.path.abspath('memo.txt')
'/Users/csev/memo.txt'
>>> os.path.exists('memo.txt')
True
>>> os.path.isdir('memo.txt')
False
>>> os.path.isdir('musica')
True
>>> os.listdir(cwd)
['musica', 'fotos', 'memo.txt']
16.2 Ejemplo: Limpieza de un directorio de fotos
Hace algún tiempo, construí un software parecido a Flickr, que recibía fotos
desde mi teléfono móvil y las almacenaba en mi servidor. Lo escribí antes de
que Flickr existiera y he continuado usándolo después, porque quería
mantener las copias originales de mis imágenes para siempre.
También quería enviar una descripción sencilla, con una línea de texto en el
mensaje MMS, en la línea de título del correo. Almacené esos mensajes en
un fichero de texto en el mismo directorio que el fichero con la imagen. Se
me ocurrió una estructura de directorios basada en el mes, año, día y hora en
que cada foto había sido realizada. Lo siguiente sería un ejemplo del nombre
de una foto y su descripción:
./2006/03/24-03-06_2018002.jpg
./2006/03/24-03-06_2018002.txt
import os
contador = 0
for (nombredir, dirs, ficheros) in os.walk('.'):
for nombrefichero in ficheros:
if nombrefichero.endswith('.txt') :
contador = contador + 1
print 'Ficheros:', contador
python txtcount.py
Ficheros: 1917
Una vez que tenemos una noción acerca de cuántos archivos terminan por
“.txt”, lo siguiente es intentar determinar automáticamente desde Python qué
ficheros son incorrectos y cuáles están bien. De modo que escribimos un
programa sencillo para imprimir en pantalla los nombres de los ficheros y el
tamaño de cada uno:
import os
from os.path import join
for (nombredir, dirs, ficheros) in os.walk('.'):
for nombrefichero in ficheros:
if nombrefichero.endswith('.txt') :
elfichero =
os.path.join(nombredir,nombrefichero)
print os.path.getsize(elfichero), elfichero
Ahora en vez de simplemente contar los ficheros, creamos un nombre de
archivo concatenando el nombre del directorio con el nombre del archivo,
usando os.path.join. Es importante usar os.path.join en vez de una
simple concatenación de cadenas, porque en Windows para construir las
rutas de archivos se utiliza la barra-invertida (\), mientras que en Linux o
Apple se usa la barra normal (/). os.path.join conoce esas diferencias y
sabe en qué sistema se está ejecutando, de modo que realiza la
concatenación correcta dependiendo del sistema. Así el mismo código de
Python puede ejecutarse tanto en Windows como en sistemas de estilo Unix.
Una vez que tenemos el nombre del fichero completo con la ruta del
directorio, usamos la utilidad os.path.getsize para obtener el tamaño e
imprimirlo en pantalla, produciendo la salida siguiente:
python txtsize.py
...
18 ./2006/03/24-03-06_2303002.txt
22 ./2006/03/25-03-06_1340001.txt
22 ./2006/03/25-03-06_2034001.txt
...
2565 ./2005/09/28-09-05_1043004.txt
2565 ./2005/09/28-09-05_1141002.txt
...
2578 ./2006/03/27-03-06_1618001.txt
2578 ./2006/03/28-03-06_2109001.txt
2578 ./2006/03/29-03-06_1355001.txt
...
<html>
<head>
<title>T-Mobile</title>
...
import os
from os.path import join
for (nombredir, dirs, ficheros) in os.walk('.'):
for nombrefichero in ficheros:
if nombrefichero.endswith('.txt') :
elfichero =
os.path.join(nombredir,nombrefichero)
tamano = os.path.getsize(elfichero)
if tamano == 2578 or tamano == 2565:
continue
manf = open(elfichero,'r')
lineas = list()
for linea in manf:
lineas.append(linea)
manf.close()
if len(lineas) > 1:
print len(lineas), elfichero
print lineas[:4]
Usamos un continue para omitir los ficheros con los dos “tamaños
incorrectos”, a continuación vamos abriendo el resto de los archivos,
pasamos las líneas de cada uno de ellos a una lista de Python y si el archivo
tiene más de una línea imprimimos en pantalla el número de líneas que
contiene y el contenido de las tres primeras.
Parece que filtrando esos ficheros con los tamaños incorrectos, y asumiendo
que todos los que tienen sólo una línea son correctos, se consiguen unos
datos bastante limpios:
python txtcheck.py
3 ./2004/03/22-03-04_2015.txt
['Little horse rider\r\n', '\r\n', '\r']
2 ./2004/11/30-11-04_1834001.txt
['Testing 123.\n', '\n']
3 ./2007/09/15-09-07_074202_03.txt
['\r\n', '\r\n', 'Sent from my iPhone\r\n']
3 ./2007/09/19-09-07_124857_01.txt
['\r\n', '\r\n', 'Sent from my iPhone\r\n']
3 ./2007/09/20-09-07_115617_01.txt
...
Pero existe aún un tipo de fichero molesto: hay algunos archivos con tres
líneas que se han colado entre mis datos y que contienen dos líneas en blanco
seguidas por una línea que dice “Sent from my iPhone”. De modo que
haremos el siguiente cambio al programa para tener en cuenta esos ficheros
también:
lineas = list()
for linea in manf:
lineas.append(linea)
if len(lineas) == 3 and
lineas[2].startswith('Sent from my iPhone'):
continue
if len(lineas) > 1:
print len(lineas), elfichero
print lineas[:4]
python txtcheck2.py
3 ./2004/03/22-03-04_2015.txt
['Little horse rider\r\n', '\r\n', '\r']
2 ./2004/11/30-11-04_1834001.txt
['Testing 123.\n', '\n']
2 ./2006/03/17-03-06_1806001.txt
['On the road again...\r\n', '\r\n']
2 ./2006/03/24-03-06_1740001.txt
['On the road again...\r\n', '\r\n']
Ahora estamos preparados para eliminar los ficheros, así que vamos a
invertir la lógica y en lugar de imprimir en pantalla los ficheros correctos
que quedan, vamos a imprimir los “erróneos” que vamos a eliminar.
import os
from os.path import join
for (nombredir, dirs, ficheros) in os.walk('.'):
for nombrefichero in ficheros:
if nombrefichero.endswith('.txt') :
elfichero =
os.path.join(nombredir,nombrefichero)
tamano = os.path.getsize(elfichero)
if tamano == 2578 or tamano == 2565:
print 'T-Mobile:',elfichero
continue
manf = open(elfichero,'r')
lineas = list()
for linea in manf:
lineas.append(linea)
manf.close()
if len(lineas) == 3 and
lineas[2].startswith('Sent from my iPhone'):
print 'iPhone:', elfichero
continue
Ahora podemos ver una lista de ficheros candidatos al borrado, junto con el
motivo por el que van a ser eliminados. El programa produce la salida
siguiente:
python txtcheck3.py
...
T-Mobile: ./2006/05/31-05-06_1540001.txt
T-Mobile: ./2006/05/31-05-06_1648001.txt
iPhone: ./2007/09/15-09-07_074202_03.txt
iPhone: ./2007/09/15-09-07_144641_01.txt
iPhone: ./2007/09/19-09-07_124857_01.txt
...
Una vez hemos comprobado que ésta es la lista de los archivos que de
verdad queremos eliminar, realizamos los cambios siguientes en el
programa:
python txtdelete.py
T-Mobile: ./2005/01/02-01-05_1356001.txt
T-Mobile: ./2005/01/02-01-05_1858001.txt
...
python txtcount.py
Ficheros: 1018
El problema que necesites resolver puede ser bastante sencillo, y quizás sólo
tengas que comprobar los nombres de los ficheros. O tal vez necesites leer
cada fichero completo y buscar ciertos patrones en el interior del mismo. A
veces necesitarás leer todos los ficheros y realizar un cambio en algunos de
ellos. Todo esto resulta bastante sencillo una vez que comprendes cómo
utilizar os.walk y las otras utilidades os.
python words.py
Introduce fichero: mbox-short.txt
...
Podemos colocar cadenas adicionales después del nombre del fichero que
contiene el código de Python y acceder a esos argumentos de línea de
comandos desde el propio programa Python. Aquí tenemos un programa
sencillo que ilustra la lectura de argumentos desde la línea de comandos:
import sys
print 'Cantidad:', len(sys.argv)
print 'Tipo:', type(sys.argv)
for arg in sys.argv:
print 'Argumento:', arg
Hay tres argumentos que se han pasado a nuestro programa, en forma de lista
con tres elementos. El primer elemento de la lista es el nombre del fichero
(argtest.py) y los otros son los dos argumentos de línea de comandos que
hemos escrito detrás del nombre del fichero.
import sys
nombre = sys.argv[1]
manejador = open(nombre, 'r')
texto = manejador.read()
print nombre, 'tiene', len(texto), 'bytes'
Cuando hayas terminado, debes cerrar la tubería como harías con un fichero:
El valor de retorno es el estado final del proceso ls; None significa que ha
terminado con normalidad (sin errores).
16.5 Glosario
16.6 Ejercicios
Ejercicio 1
En una colección extensa de archivos MP3 puede haber más de una copia
de la misma canción, almacenadas en distintos directorios o con nombres
de archivo diferentes. El objetivo de este ejercicio es buscar esos
duplicados.
import hashlib
...
manf = open(elfichero,'r')
datos = manf.read()
manf.close()
checksum =
hashlib.md5(datos).hexdigest()
./2004/11/15-11-04_0923001.jpg ./2004/11/15-11-
04_1016001.jpg
./2005/06/28-06-05_1500001.jpg ./2005/06/28-06-
05_1502001.jpg
./2006/08/11-08-06_205948_01.jpg ./2006/08/12-08-
06_155318_02.jpg
1
“walk” significa “recorrer” (Nota del trad.)
2
Cuando se usan tuberías para comunicarse con comandos del sistema
operativo como ls, es importante que sepas qué sistema operativo estás
utilizando y que sólo abras tuberías hacia comandos que estén
soportados en ese sistema operativo.
Apéndice A Programando con Python en Windows
En este apéndice, mostraremos una serie de pasos para que puedas ejecutar
Python en Windows. Existen muchos métodos diferentes que se pueden
seguir, y éste es sólo uno de ellos que intenta hacer las cosas de una forma
sencilla.
https://notepad-plus-plus.org/
https://www.python.org/downloads/
Una vez hayas instalado Python, deberías tener una carpeta nueva en tu
equipo como C:\Python27.
C:\Users\csev\> cd Desktop
C:\Users\csev\Desktop> cd py4inf
Luego teclea
C:\Users\csev\Desktop\py4inf> dir
C:\Users\csev\Desktop\py4inf> prog1.py
Hola, Chuck
C:\Users\csev\Desktop\py4inf>
En este apéndice, mostraremos una serie de pasos para que puedas ejecutar
Python en Macintosh. Dado que Python ya viene incluido en el sistema
Operativo Macintosh, sólo tenemos que aprender cómo editar ficheros y
ejecutar programas de Python en la ventana del terminal.
tienes que estar en la carpeta que contiene tu programa en Python para poder
ejecutarlo. Usa el comando cd para moverte a una nueva carpeta y luego usa
el comando ls para mostrar un listado de los ficheros de esa carpeta.
(Allen B. Downey)
Me di cuenta de que uno de los problemas eran los libros. Eran demasiado
grandes, con demasiados detalles innecesarios de Java, y sin suficiente
orientación de alto nivel sobre cómo programar. Y todos ellos sufrían el
mismo efecto trampilla: comenzaban siendo muy fáciles, avanzaban poco a
poco, y en algún lugar alrededor del Capítulo 5 el suelo desaparecía. Los
estudiantes recibían demasiado material nuevo demasiado rápido, y yo tenía
que pasar el resto del semestre recogiendo los pedazos.
Dos semanas antes del primer día de clase, decidí escribir mi propio libro.
Mis objetivos eran:
Espero que disfrutes con este libro, y que te ayude a aprender a programar y
a pensar, al menos un poquito, como un informático.
(Allen B. Downey)
Quiero dar las gracias también a Chris Meyers, que ha contribuído en varias
secciones de How to Think Like a Computer Scientist.
Y quiero dar las gracias a mi mujer, Lisa, por su trabajo en este libro, en
Green Tea Press, y por todo lo demás, también.
Allen B. Downey
Needham MA
(Allen B. Downey)
Lloyd Hugh Allen, Yvon Boulianne, Fred Bremmer, Jonah Cohen, Michael
Conlon, Benoit Girard, Courtney Gleason and Katherine Smith, Lee Harr,
James Kaylin, David Kershaw, Eddie Lam, Man-Yong Lee, David Mayo,
Chris McAloon, Matthew J. Moelter, Simon Dicon Montford, John Ouzts,
Kevin Parks, David Pool, Michael Schmitt, Robin Shaw, Paul Sleigh, Craig
T. Snydal, Ian Thomas, Keith Verheyden, Peter Winstanley, Chris Wrobel,
Moshe Zadka, Christoph Zwerschke, James Mayer, Hayden McAfee, Angel
Arnal, Tauhidul Hoque and Lex Berezhny, Dr. Michele Alzetta, Andy
Mitchell, Kalin Harvey, Christopher P. Smith, David Hutchins, Gregor Lingl,
Julie Peters, Florin Oprina, D. J. Webre, Ken, Ivo Wever, Curtis Yanko, Ben
Logan, Jason Armstrong, Louis Cordier, Brian Cain, Rob Black, Jean-
Philippe Rey at Ecole Centrale Paris, Jason Mader at George Washington
University made a number Jan Gundtofte-Bruun, Abel David and Alexis
Dinno, Charles Thayer, Roger Sperberg, Sam Bull, Andrew Cheung, C.
Corey Capel, Alessandra, Wim Champagne, Douglas Wright, Jared Spindor,
Lin Peiheng, Ray Hagtvedt, Torsten Hübsch, Inga Petuhhov, Arne
Babenhauserheide, Mark E. Casida, Scott Tyler, Gordon Shephard, Andrew
Turner, Adam Hobart, Daryl Hammond and Sarah Zimmerman, George Sass,
Brian Bingham, Leah Engelbert-Fenton, Joe Funke, Chao-chao Chen, Jeff
Paine, Lubos Pintes, Gregg Lind and Abigail Heithoff, Max Hailperin,
Chotipat Pornavalai, Stanislaw Antol, Eric Pashman, Miguel Azevedo,
Jianhua Liu, Nick King, Martin Zuther, Adam Zimmerman, Ratnakar Tiwari,
Anurag Goel, Kelli Kratzer, Mark Griffiths, Roydan Ongie, Patryk
Wolowiec, Mark Chonofsky, Russell Coleman, Wei Huang, Karen Barber,
Nam Nguyen, Stéphane Morin, Fernando Tardío, y Paul Stoop.
Apéndice D Detalles del Copyright
Charles Severance
www.dr-chuck.com
Ann Arbor, MI, USA
9 de Septiembre de 2013
Index
XML, 13.9