Introprog Py2 PDF
Introprog Py2 PDF
Python (2ed)
Facultad de Ingeniería
Rector: Luis Felipe Gómez Restrepo, S.J.
Vicerrectora Académica: Ana Milena Yoshioka Vargas
Vicerrector del Medio Universitario: Luis Fernando Granados Ospina, S.J.
Facultad de Ingeniería
Decano Académico: Jaime Alberto Aguilar Zambrano Ph.D
Directora del departamento de Ingeniería electrónica y computación: Gloria Inés Alvarez Vargas
ISBN: 978-958-8347-22-6
Formato 17 x 25 cms
ISBN 978-958-8347-22-6
Prólogo XV
Prefacio XIX
1. Solución de problemas 1
1.1. Solución de acertijos . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2. El método de solución . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3. Reflexión sobre este método de solución . . . . . . . . . . . . . . . . 7
1.4. Acertijos propuestos . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.5. Mas allá de los acertijos: problemas computacionales . . . . . . . . 9
1.6. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.7. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
V
VI ÍNDICE GENERAL
4. Funciones 39
4.1. Llamadas a funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
4.2. Conversión de tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
4.3. Coerción de tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.4. Funciones matemáticas . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.5. Composición . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.6. Agregando nuevas funciones . . . . . . . . . . . . . . . . . . . . . . 43
4.7. Definiciones y uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
4.8. Flujo de ejecución . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
4.9. Parámetros y argumentos . . . . . . . . . . . . . . . . . . . . . . . . 47
4.10. Las variables y los parámetros son locales . . . . . . . . . . . . . . . 48
4.11. Diagramas de pila . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
4.12. Funciones con resultados . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.13. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.14. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
5. Condicionales y recursión 55
5.1. El operador residuo . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
5.2. Expresiones booleanas . . . . . . . . . . . . . . . . . . . . . . . . . . 56
5.3. Operadores lógicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
ÍNDICE GENERAL VII
6. Funciones fructíferas 69
6.1. Valores de retorno . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
6.2. Desarrollo de programas . . . . . . . . . . . . . . . . . . . . . . . . . 71
6.3. Composición . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
6.4. Funciones booleanas . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
6.5. Más recursión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
6.6. El salto de fe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
6.7. Un ejemplo más . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
6.8. Chequeo de tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
6.9. Pruebas unitarias con doctest . . . . . . . . . . . . . . . . . . . . . . 81
6.10. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
6.11. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
7. Iteración 85
7.1. Asignación múltiple . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
7.2. La sentencia while (mientras) . . . . . . . . . . . . . . . . . . . . 86
7.3. Tablas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
7.4. Tablas de dos dimensiones . . . . . . . . . . . . . . . . . . . . . . . . 91
7.5. Encapsulamiento y generalización . . . . . . . . . . . . . . . . . . . 92
7.6. Más encapsulamiento . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
7.7. Variables locales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
7.8. Mas generalización . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
VIII ÍNDICE GENERAL
7.9. Funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
7.10. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
7.11. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
8. Cadenas 101
8.1. Un tipo de dato compuesto . . . . . . . . . . . . . . . . . . . . . . . 101
8.2. Longitud . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
8.3. Recorridos en cadenas y el ciclo for . . . . . . . . . . . . . . . . . . 102
8.4. Segmentos de cadenas . . . . . . . . . . . . . . . . . . . . . . . . . . 104
8.5. Comparación de cadenas . . . . . . . . . . . . . . . . . . . . . . . . . 105
8.6. Las cadenas son inmutables . . . . . . . . . . . . . . . . . . . . . . . 106
8.7. Una función buscar . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
8.8. Iterando y contando . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
8.9. El módulo string . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
8.10. Clasificación de caracteres . . . . . . . . . . . . . . . . . . . . . . . . 108
8.11. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
8.12. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
9. Listas 115
9.1. Creación de listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
9.2. Accediendo a los elementos . . . . . . . . . . . . . . . . . . . . . . . 116
9.3. Longitud de una lista . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
9.4. Pertenencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
9.5. Listas y ciclos for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
9.6. Operaciones sobre listas . . . . . . . . . . . . . . . . . . . . . . . . . 120
9.7. Segmentos de listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
9.8. Las listas son mutables . . . . . . . . . . . . . . . . . . . . . . . . . . 121
9.9. Otras operaciones sobre listas . . . . . . . . . . . . . . . . . . . . . . 122
9.10. Objetos y valores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
9.11. Alias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
9.12. Clonando listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
9.13. Listas como parámetros . . . . . . . . . . . . . . . . . . . . . . . . . 125
9.14. Listas anidadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
9.15. Matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
ÍNDICE GENERAL IX
A. Depuración 263
A.1. Errores sintácticos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
A.2. Errores en tiempo de ejecución . . . . . . . . . . . . . . . . . . . . . 265
A.3. Errores semánticos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
XV
XVI Prólogo
son importantes para los ingenieros de software y para los estudiantes de ciencias
de la computación, usar este enfoque hace la informática muy aburrida. Cuando
enseño un curso no quiero tener un grupo de estudiantes sin inspiración. Quisie-
ra verlos intentando resolver problemas interesantes, explorando ideas diferentes,
intentando enfoques no convencionales, rompiendo reglas y aprendiendo de sus
errores. En el proceso no quiero perder la mitad del semestre tratando de resolver
problemas sintácticos oscuros, interpretando mensajes de error del compilador in-
comprensibles, o descifrando cuál de las muchas maneras en que un programa
puede generar un error grave de memoria se está presentando.
Una de las razones del por qué me gusta Python es que proporciona un equili-
brio muy bueno entre lo práctico y lo conceptual. Puesto que se interpreta Python,
los principiantes pueden empezar a hacer cosas interesantes casi de inmediato sin
perderse en problemas de compilación y enlace. Además, Python viene con una
biblioteca grande de módulos, que pueden ser usados en dominios que van des-
de programación en la web hasta aplicaciones gráficas. Tener un foco práctico es
una gran manera de enganchar a los estudiantes y permite que emprendan proyec-
tos significativos. Sin embargo, Python también puede servir como una excelente
base para introducir conceptos importantes de la informática. Puesto que Python
soporta completamente procedimientos y clases, los estudiantes pueden ser intro-
ducidos gradualmente a temas como la abstracción procedimental, las estructuras
de datos y la programación orientada a objetos—lo que se puede aplicar después
a cursos posteriores en Java o C++. Python proporciona, incluso, varias caracterís-
ticas de los lenguajes de programación funcionales y puede usarse para introducir
conceptos que se pueden explorar con más detalle en cursos con Scheme y Lisp.
Leyendo, el prefacio de Jeffrey, estoy sorprendido por sus comentarios de que Pyt-
hon le permita ver un “más alto nivel de éxito y un nivel bajo de frustración” y
que puede “avanzar mas rápido con mejores resultados.” Aunque estos comenta-
rios se refieren a sus cursos introductorios, a veces uso Python por estas mismas
razones en los cursos de informática avanzada en la Universidad de Chicago. En
estos cursos enfrento constantemente la tarea desalentadora de cubrir un montón
de material difícil durante nueve semanas. Aunque es totalmente posible para mi
infligir mucho dolor y sufrimiento usando un lenguaje como C++, he encontra-
do a menudo que este enfoque es improductivo—especialmente cuando el curso
se trata de un asunto sin relación directa con la “programación.” He encontrado
XVII
que usar Python me permite enfocar el tema del curso y dejar a los estudiantes
desarrollar proyectos substanciales.
Aunque Python siga siendo un lenguaje joven y en desarrollo, creo que tiene un
futuro brillante en la educación. Este libro es un paso importante en esa dirección.
David Beazley
Universidad de Chicago, Autor de Python Essential Reference
XVIII Prólogo
Prefacio
XIX
XX Prefacio
Trabajar en este libro, por los dos últimos años, ha sido una recompensa para mis
estudiantes y para mí; y mis estudiantes tuvieron una gran participación en el pro-
ceso. Puesto que podía realizar cambios inmediatos, siempre que alguien encon-
trara un error de deletreo o un paso difícil, yo les animaba a que buscaran errores
en el libro, dándoles un punto cada vez que hicieran una sugerencia que resultara
en un cambio en el texto. Eso tenía la ventaja doble de animarles a que leyeran el
texto más cuidadosamente y de conseguir la corrección del texto por sus lectores
críticos más importantes, los estudiantes usándolo para aprender informática.
#include <iostream.h>
void main()
{
cout << "Hola, mundo." << endl;
}
Aunque este es un ejemplo trivial, las ventajas de Python salen a la luz. El curso de
Informática I, en Yorktown, no tiene prerrequisitos, es por eso que muchos de los
estudiantes, que ven este ejemplo, están mirando a su primer programa. Algunos
de ellos están un poco nerviosos, porque han oído que la programación de compu-
tadores es difícil de aprender. La versión C++ siempre me ha forzado a escoger
entre dos opciones que no me satisfacen: explicar el #include, void main(), y las
sentencias {, y } y arriesgar a confundir o intimidar a algunos de los estudiantes al
principio, o decirles, “No te preocupes por todo eso ahora; lo retomaré más tarde,”
y tomar el mismo riesgo. Los objetivos educativos en este momento del curso son
introducir a los estudiantes la idea de sentencia y permitirles escribir su primer
programa. Python tiene exactamente lo que necesito para lograr esto, y nada más.
Comparar el texto explicativo de este programa en cada versión del libro ilustra
más de lo que esto significa para los estudiantes principiantes. Hay trece párrafos
de explicación de “Hola, mundo!” en la versión C++; en la versión Python, solo hay
dos. Aún mas importante, los 11 párrafos que faltan no hablan de “grandes ideas”
XXIII
bilidad de crear programas significativos; esto genera una actitud positiva hacia la
experiencia de la programación.
http://www.thinkpython.com
Con la publicación del libro, en forma impresa, espero que continúe y se acelere el
crecimiento de esta comunidad de usuarios.
La emergencia de esta comunidad y la posibilidad que sugiere para otras experien-
cias de colaboración similar entre educadores han sido las partes más excitantes de
trabajar en este proyecto, para mí. Trabajando juntos, nosotros podemos aumentar
la calidad del material disponible para nuestro uso y ahorrar tiempo valioso.
Yo les invito a formar parte de nuestra comunidad y espero escuchar de ustedes.
Por favor escriba a los autores a [email protected].
Jeffrey Elkner
Escuela Secundaria Yortown
Arlington, Virginia.
Lista de los colaboradores
Este libro vino a la luz debido a una colaboración que no sería posible sin la licen-
cia de documentación libre de la GNU (Free Documentation License). Quisiéramos
agradecer a la Free Software Foundation por desarrollar esta licencia y, por supues-
to, por ponerla a nuestra disposición.
Nosotros queremos agradecer a los mas de 100 juiciosos y reflexivos lectores que
nos han enviado sugerencias y correcciones durante los años pasados. En el espí-
ritu del software libre, decidimos expresar nuestro agradecimiento en la forma de
una lista de colaboradores. Desafortunadamente, esta lista no está completa, pero
estamos haciendo nuestro mejor esfuerzo para mantenerla actualizada.
Si tiene la oportunidad de leer la lista, tenga en cuenta que cada persona mencio-
nada aquí le ha ahorrado a usted y a todos los lectores subsecuentes la confusión
debida a un error técnico o debido a una explicación confusa, solo por enviarnos
una nota.
Después de tantas correcciones, todavía pueden haber errores en este libro. Si ve
uno, esperamos que tome un minuto para contactarnos. El correo electrónico es
[email protected]. Si hacemos un cambio debido a su sugerencias, usted
aparecerá en la siguiente versión de la lista de colaboradores (a menos que usted
pida ser omitido). Gracias!
Lloyd Hugh Allen remitió una corrección a la Sección 8.4.
Jonah Cohen escribió los guiones en Perl para convertir la fuente LaTeX, de
este libro, a un maravilloso HTML.
XXV
XXVI Lista de los colaboradores
Lee Harr sometió más correcciones de las que tenemos espacio para enu-
merar aquí, y, por supuesto, debería ser listado como uno de los editores
principales del texto.
Simon Dicon Montford reportó una definición de función que faltaba y varios
errores en el Capítulo 3. Él también encontró errores en la función incrementar
del Capítulo 13.
Kevin Parks envió sugerencias valiosas para mejorar la distribución del libro.
XXVII
Angel Arnal
I Juanes
Litza Amurrio
Efrain Andia
Ellos habían traducido los capítulos 1,2,10,11, y 12, así como el prefacio, la intro-
ducción y la lista de colaboradores. Tomé su valioso trabajo como punto de parti-
da, adapté los capítulos, traduje las secciones faltantes del libro y añadí un primer
capítulo adicional sobre solución de problemas.
Aunque el libro traduce la primera edición del original, todo se ha corregido pa-
ra que sea compatible con Python 2.7, por ejemplo se usan booleanos en vez de
enteros en los condicionales y ciclos.
Para realizar este trabajo ha sido invaluable la colaboración de familiares, colegas,
amigos y estudiantes que han señalado errores, expresiones confusas y han aporta-
do toda clase de sugerencias constructivas. Mi agradecimiento va para los traduc-
tores antes mencionados y para los estudiantes de Biología que tomaron el curso
de Informática en la Pontificia Universidad Javeriana (Cali-Colombia), durante el
semestre 2014-1:
Estefanía Lopez
XXIX
XXX Traducción al español
Gisela Chaves
Marlyn Zuluaga
Francisco Sanchez
Diana Ramirez
Guillermo Perez
Sara Rodriguez
Claudia Escobar
Yisveire Fontalvo
Solución de problemas
1 2 3
4 5 6
7 8 9
Usted puede notar que esta solución candidata no es correcta. Si tomamos la suma
por filas, obtenemos valores distintos:
1
2 Solución de problemas
En la fila 1: 1+2+3=6
En la fila 2: 4+5+6=15
En la fila 3: 7+8+9=24
En la columna 1: 1+4+7=12
En la columna 2: 2+5+8=15
En la columna 3: 3+6+9=18
En la diagonal 1: 1+5+9=15
En la diagonal 2: 7+5+3=15
Tómese un par de minutos para resolver este acertijo, es decir, para construir un
cuadrado mágico y regrese a la lectura cuando obtenga la solución.
Ahora, responda para sí mismo las siguientes preguntas:
4 9 2
3 5 7
8 1 6
Esta solución es correcta, porque la suma por filas, columnas y de las dos diagona-
les da el mismo valor, 15. Ahora, para demostrarle a alguien este hecho podemos
revisar las sumas por filas, columnas y diagonales detalladamente.
El proceso de solución que llevamos a cabo fue el siguiente:
1 2 3
4 5 6
7 8 9
Una posible estrategia consiste en colocar parejas de números que sumen 10,
dejando al 5 “emparedado”, por ejemplo, poner la pareja 6,4:
6
5
4
Un primer ensayo:
4 Solución de problemas
7 6 2
5
8 4 3
Para deducir más hechos, a partir de los datos recolectados y los detalles
inusuales, hay que usar todas las armas del razonamiento: el poder de la de-
ducción (derivar nuevos hechos a partir de los datos), la inducción (generali-
zar a partir de casos), la refutación (el proceso de probar la falsedad de algu-
na conjetura), el pensamiento analógico (encontrando relaciones,metáforas,
analogías) y, por último, pero no menos importante, el uso del sentido co-
mún.
Después del análisis de datos, uno siempre debe proponer una alternativa de
solución, así sea simple e ingenua, y proceder intentando probarla y refutarla
al mismo tiempo. Vale la pena recalcar esto: no importa que tan ingenua, sen-
cilla e incompleta es una alternativa de solución, con tal de que nos permita
seguir indagando. Esto es como la primera frase que se le dice a una chica
(o chico, dado el caso) que uno quiere conocer; no importa qué frase sea, no
importa que tan trivial sea, con tal de que permita iniciar una conversación.
6 Solución de problemas
7 6 2
5
8 4 3
a = b
2
a = ba
a2 − b2 = ba − b2
(a − b)(a + b) = b(a − b)
a+b = b
a = 2b
a
= 2
b
1 = 2
3 4
× 2 1
3 4
+ 6 8
7 1 4
p1 p2
× q1 q2
q2 p1 q2 p2
+ q1 p1 q1 p2
q1 p1 q2 p1 + q1 p2 q2 p2
Tome la cifra q2 y multiplíquela por las cifras de P (con ayuda de una tabla
de multiplicación). Ubique los resultados debajo de cada cifra de P corres-
10 Solución de problemas
pondiente.
Tome la cifra q1 y multiplíquela por las cifras de P (con ayuda de una tabla de
multiplicación). Ubique los resultados debajo de las cifras que se generaron
en el paso anterior, aunque desplazadas una columna hacia la izquierda.
Usted puede estar quejándose en este momento, ¿para qué hay que complicar tanto
nuestro viejo y conocido proceso de multiplicación? Bueno, hay varias razones
para esto:
Una descripción impersonal como ésta puede ser leída y ejecutada por cual-
quier persona —o computador, como veremos mas adelante—.
1.6. Glosario
Acertijo: enigma o adivinanza que se propone como pasatiempo.
Problema computacional: una situación general con una especificación de los da-
tos de entrada y los datos de salida deseados.
1.7. Ejercicios
Intente resolver los siguientes problemas computacionales, proponiendo solucio-
nes generales e impersonales:
A cierto nivel, usted aprenderá a programar, lo cual es una habilidad muy útil
por sí misma. A otro nivel, usted utilizará la programación para obtener algún
resultado. Ese resultado se verá más claramente durante el proceso.
13
14 El camino hacia el programa
Código Código
Compilador Ejecutor Salida
Fuente Objeto
print 1 + 1
Por acuerdo unánime, los archivos que contienen programas de Python tienen
nombres que terminan con .py. Para ejecutar el programa, se le tiene que indicar
el nombre del guión al intérprete.
$ python unomasuno.py
2
Repetición ejecutar alguna acción repetidas veces, usualmente con alguna varia-
ción.
Aunque sea difícil de creer, todos los programas en existencia son formulados ex-
clusivamente con tales instrucciones. Así, una manera de describir la programa-
ción es: el proceso de romper una tarea en tareas cada vez más pequeñas hasta que
éstas sean lo suficientemente sencillas como para ser ejecutadas con una secuencia
de estas instrucciones básicas.
Quizás esta descripción es un poco ambigua. No se preocupe. Explicaremos ésto
con más detalle en el tema de algoritmos.
2.3 ¿Qué es la depuración (debugging)? 17
El segundo tipo de error es el de tiempo de ejecución. Este error aparece sólo cuan-
do se ejecuta un programa. Estos errores también se llaman excepciones, porque
indican que algo excepcional ha ocurrido.
Con los programas que vamos a escribir al principio, los errores de tiempo de
ejecución ocurrirán con poca frecuencia.
18 El camino hacia el programa
Los lenguajes formales casi siempre tienen reglas sintácticas estrictas. Por ejemplo,
3+3 = 6 es una expresión matemática correcta, pero 3 = +6$ no lo es. De la misma
manera, H2 O es una nomenclatura química correcta, pero 2 Zz no lo es.
Existen dos clases de reglas sintácticas, en cuanto a unidades y estructura. Las
unidades son los elementos básicos de un lenguaje, como lo son las palabras, los
números y los elementos químicos. Por ejemplo, en 3=+6$, $ no es una unidad ma-
temática aceptada. Similarmente, 2 Zz no es formal porque no hay ningún elemento
químico con la abreviación Zz.
La segunda clase de error sintáctico está relacionado con la estructura de un ele-
mento; mejor dicho, el orden de las unidades. La estructura de la sentencia 3=+6$
no es aceptada porque no se puede escribir el símbolo de igualdad seguido de un
símbolo más. Similarmente, las fórmulas moleculares tienen que mostrar el núme-
ro de subíndice después del elemento, no antes.
Al leer una oración, sea en un lenguaje natural o una sentencia en un lenguaje
técnico, se debe discernir la estructura de la oración. En un lenguaje natural este
proceso, llamado análisis sintáctico, ocurre subconscientemente.
Por ejemplo cuando se escucha una oración simple como “el otro zapato se cayó”,
se puede distinguir el sustantivo “el otro zapato” y el predicado “se cayó”. Cuando
20 El camino hacia el programa
Poesía: se utiliza una palabra por su cualidad auditiva tanto como por su signifi-
cado. El poema, en su totalidad, produce un efecto o reacción emocional. La
ambigüedad no es sólo común, sino utilizada a propósito.
Este es un ejemplo de una sentencia print, la cual no imprime nada en papel, más
bien muestra un valor. En este caso, el resultado es mostrar en pantalla las palabras:
2.6. Glosario
Solución de problemas: el proceso de formular un problema, hallar la solución y
expresarla.
Lenguaje de alto nivel: un lenguaje como Python que es diseñado para ser fácil
de leer y escribir por la gente.
22 El camino hacia el programa
Código fuente: un programa escrito en un lenguaje de alto nivel antes de ser com-
pilado.
Código objeto: la salida del compilador una vez que el programa ha sido traduci-
do.
Programa ejecutable: otro nombre para el código de objeto que está listo para ser
ejecutado.
Error sintáctico: un error estructural que hace que un programa sea imposible de
analizar sintácticamente (e imposible de interpretar).
Error semántico: un error en un programa que hace que ejecute algo que no era lo
deseado.
Sentencia print: una instrucción que causa que el intérprete de Python muestre
un valor en el monitor.
2.7. Ejercicios
En los ejercicios 1,2,3 y 4 escriba una oración en español:
6. ¿Qué sucede si utiliza el signo de división (/)? ¿Son los resultados obtenidos
los esperados? Explique.
Muchas veces Python indica la ubicación del error de sintaxis, sin embargo,
no siempre es precisa, por lo que no proporciona suficiente información sobre
cuál es el problema. De esta manera, el mejor antídoto es que usted apren-
da la sintaxis de Python. En este caso, Python protesta porque no encuentra
signo de operación alguno entre los números.
Escriba una entrada que produzca un mensaje de error cuando se introduzca
en el intérprete de Python. Explique por qué no tiene una sintaxis válida.
8. Escriba print ’hola’. Python ejecuta esta sentencia que muestra las letras
h-o-l-a. Nótese que las comillas simples en los extremos de la cadena no son
parte de la salida mostrada. Ahora escriba print ’"hola"’ y describa y ex-
plique el resultado.
11. Ahora cree un guión de Python con el nombre prueba1.py que contenga lo
siguiente (asegúrese de guardar el archivo antes de intentar ejecutarlo): ’Esta
es una prueba...’
2.7 Ejercicios 25
12. Ahora cambie el contenido del guión a: print ’Esta es una prueba...’ y
ejecutelo de nuevo.
¿Qué pasó esta vez?
Cuando se escribe una expresión en el intérprete de Python, ésta es evalua-
da y el resultado es mostrado en la línea siguiente. ’Esta es una prueba...’ es
una expresión, que se evalúa a ’Esta es una prueba...’ (de la misma mane-
ra que 42 se evalúa a 42). Sin embargo, la evaluación de expresiones en un
guión no se envía a la salida del programa, por lo que es necesario mostrarla
explícitamente.
26 El camino hacia el programa
Capítulo 3
Variables, expresiones y
sentencias
>>> print 4
4
Si no está seguro del tipo que un valor tiene, el intérprete le puede decir.
27
28 Variables, expresiones y sentencias
Sin despertar ninguna sorpresa, las cadenas pertenecen al tipo string (cadena) y
los enteros pertenecen al tipo int. Menos obvio, los números con cifras decimales
pertenecen a un tipo llamado float, porque éstos se representan en un formato
denominado punto flotante.
¿Qué ocurre con valores como "17" y "3.2"? Parecen números, pero están ence-
rrados entre comillas como las cadenas.
¡Bueno, eso no es lo que esperábamos!. Resulta que 1,000,000 es una tupla, algo
que encontraremos en el Capítulo 11. De momento, recuerde no poner comas en
sus números enteros.
3.2. Variables
Una de las características más poderosas en un lenguaje de programación es la
capacidad de manipular variables. Una variable es un nombre que se refiere a un
valor.
La sentencia de asignación crea nuevas variables y les da valores:
Este ejemplo hace tres asignaciones: la primera asigna la cadena "¿Qué Onda?" a
una nueva variable denominada mensaje, la segunda le asigna el entero 17 a n y la
tercera le asigna el número de punto flotante 3.14159 a pi.
Usted puede mantener esta lista a mano. Si el intérprete se queja por alguno de sus
nombres de variables, y usted no sabe por qué, búsquelo en esta lista.
3.4 Sentencias 31
3.4. Sentencias
Una sentencia es una instrucción que el intérprete de Python puede ejecutar. He-
mos visto dos clases de sentencias: la asignación y print.
Cuando usted digita una sentencia en la línea de comandos, Python la ejecuta y
despliega el resultado, si hay alguno. El resultado de un print es un valor. Las
asignaciones no producen un resultado.
Un guión usualmente contiene una secuencia de sentencias. Si hay más de una, los
resultados aparecen uno a uno a medida que las sentencias se ejecutan.
Por ejemplo, el guión
print 1
x = 2
print x
produce la salida:
1
2
>>> 1 + 1
2
Un valor, por si mismo, se considera como una expresión, lo mismo ocurre para
las variables.
>>> 17
17
>>> x
2
32 Variables, expresiones y sentencias
Cuando Python muestra el valor de una expresión que ha evaluado, utiliza el mis-
mo formato que se usaría para entrar un valor. En el caso de las cadenas, esto
implica que se incluyen las comillas. Cuando se usa la sentencia print, el efecto es
distinto como usted ya lo ha evidenciado.
En un guión, una expresión, por sí misma, es una sentencia legal, pero no realiza
nada. El guión:
17
3.2
" Hola , Mundo ! "
1 + 1
>>> minuto = 59
>>> minuto /60
0
Los Paréntesis tienen la precedencia más alta y pueden usarse para forzar la
evaluación de una expresión de la manera que usted desee. Ya que las expre-
siones en paréntesis se evalúan primero, 2 * (3-1) es 4, y (1+1)**(5-2) es
34 Variables, expresiones y sentencias
8. Usted también puede usar paréntesis para que una expresión quede más
legible, como en (minuto * 100) / 60, aunque esto no cambie el resultado.
mensaje -1 " Hola " /123 mensaje * " Hola " " 15 " +2
Sin embargo, el operador + funciona con cadenas, aunque no calcula lo que usted
esperaría. Para las cadenas, el operador + representa la concatenación, que signifi-
ca unir los dos operandos enlazándolos en el orden en que aparecen. Por ejemplo:
La salida de este programa es banano pan con nueces. El espacio antes de la pa-
labra pan es parte de la cadena y sirve para producir el espacio entre las cadenas
concatenadas.
3.9 Composición 35
El operador * también funciona con las cadenas; hace una repetición. Por ejemplo,
’Fun’*3 es ’FunFunFun’. Uno de los operandos tiene que ser una cadena, el otro
tiene que ser un entero.
Estas interpretaciones de + y * tienen sentido por la analogía con la suma y la multi-
plicación. Así como 4*3 es equivalente a 4+4+4, esperamos que Fun*3 sea lo mismo
que "Fun"+"Fun"+"Fun", y lo es. Sin embargo, las operaciones de concatenación y
repetición sobre cadenas tienen una diferencia significativa con las operaciones de
suma y multiplicación. ¿Puede usted pensar en una propiedad que la suma y la
multiplicación tengan y que la concatenación y repetición no?
3.9. Composición
Hasta aquí hemos considerado a los elementos de un programa—variables, expre-
siones y sentencias—aisladamente, sin especificar cómo combinarlos.
Una de las características mas útiles de los lenguajes de programación es su capa-
cidad de tomar pequeños bloques para componer con ellos. Por ejemplo, ya que
sabemos cómo sumar números y cómo imprimirlos; podemos hacer las dos cosas
al mismo tiempo:
>>> print 17 + 3
20
De hecho, la suma tiene que calcularse antes que la impresión, así que las acciones
no están ocurriendo realmente al mismo tiempo. El punto es que cualquier expre-
sión que tenga números, cadenas y variables puede ser usada en una sentencia de
impresión (print). Usted ha visto un ejemplo de esto:
print " Número de minutos desde media noche : " ,
hora *60+ minuto
Usted también puede poner expresiones arbitrarias en el lado derecho de una sen-
tencia de asignación:
porcentaje = ( minuto * 100) / 60
Esto no parece nada impresionante ahora, pero vamos a ver otros ejemplos en los
que la composición hace posible expresar cálculos complejos organizada y conci-
samente.
36 Variables, expresiones y sentencias
Advertencia: hay restricciones sobre los lugares en los que se pueden usar las ex-
presiones. Por ejemplo, el lado izquierdo de una asignación tiene que ser un nom-
bre de variable, no una expresión. Así que esto es ilegal: minuto+1 = hora.
3.10. Comentarios
A medida que los programas se hacen más grandes y complejos, se vuelven más
difíciles de leer. Los lenguajes formales son densos; y, a menudo, es difícil mirar
una sección de código y saber qué hace, o por qué lo hace.
Por esta razón, es una muy buena idea añadir notas a sus programas para expli-
car, en lenguaje natural, lo que hacen. Estas notas se denominan comentarios y se
marcan con el símbolo #:
En este caso, el comentario aparece en una línea completa. También pueden ir co-
mentarios al final de una línea:
todo lo que sigue desde el # hasta el fin de la línea se ignora—no tiene efecto en el
programa. El mensaje es para el programador que escribe el programa o para algún
programador que podría usar este código en el futuro. En este caso, le recuerda al
lector el sorprendente comportamiento de la división entera en Python.
3.11. Glosario
Valor: un número o una cadena (u otra cosa que se introduzca más adelante) que
puede ser almacenado en una variable o calculado en una expresión.
Tipo: conjunto de valores. El tipo del valor determina cómo se puede usar en ex-
presiones. Hasta aquí, los tipos que usted ha visto son enteros (tipo int),
números de punto flotante (tipo float) y cadenas (tipo string).
Sentencia: sección de código que representa un comando o acción. Hasta aquí las
sentencias que usted ha visto son la de asignación y la de impresión.
Palabra reservada: es una palabra usada por el compilador para analizar sintác-
ticamente un programa; usted no puede usar palabras reservadas como if,
def, y while como nombres de variables.
Operador: símbolo especial que representa un simple cálculo como una suma,
multiplicación o concatenación de cadenas.
División entera: operación que divide un entero por otro y retorna un entero. La
división entera retorna el número de veces que el denominador cabe en el
numerador y descarta el residuo.
Reglas de precedencia: reglas que gobiernan el orden en que las expresiones que
tienen múltiples operadores y operandos se evalúan.
3.12. Ejercicios
1. Registre qué sucede cuando usa la sentencia print en combinación con una
sentencia de asignación, por ejemplo:
print n = 7
2. ¿Que sucede cuando se usa la sentencia print con una expresión?, por ejem-
plo:
print 8+5
4. Tome la siguiente oración: Sólo trabajo y nada de juegos hacen de Juan un ni-
ño aburrido. Almacene cada palabra en variables separadas, después mues-
tre la oración en una sola línea usando la sentencia print.
>>> x = input ()
3.14
>>> type ( x )
>>> x = raw_input ()
3.14
>>> type ( x )
Funciones
Otro ejemplo es la función id que toma un valor o una variable y retorna un entero
que actúa como un identificador único:
>>> id (3)
134882108
>>> b = 3
>>> id ( b )
39
40 Funciones
134882108
int también puede convertir valores de punto flotante a enteros, pero hay que
tener en cuenta que va a eliminar la parte decimal:
Puede parecer extraño el hecho de que Python distinga el valor entero 1 del valor
en punto flotante 1.0. Pueden representar el mismo número pero tienen diferen-
tes tipos. La razón para esto es que su representación interna en la memoria del
computador es distinta.
>>> minuto = 59
>>> float ( minute )/60.0
0.983333333333
>>> minuto = 59
>>> minuto / 60.0
0.983333333333
evalúa la expresión entre paréntesis (el argumento). Por ejemplo, pi/2 es aproxi-
madamente 1.571, y 1/x es 0.1 (si x tiene el valor 10.0).
Entonces, se evalúa la función, ya sea mirando el resultado en una tabla o calculan-
do varias operaciones. El seno de 1.571 es 1, y el logaritmo de 0.1 es -1 (asumiendo
que log indica el logaritmo en base 10).
Este proceso puede aplicarse repetidamente para evaluar expresiones más compli-
cadas como log(1/sen(pi/2)). Primero se evalúa el argumento de la función más
interna, luego la función, y se continúa así.
Python tiene un módulo matemático que proporciona la mayoría de las funciones
matemáticas. Un módulo es un archivo que contiene una colección de funciones
relacionadas.
Antes de que podamos usar funciones de un módulo, tenemos que importarlas:
Para llamar a una de las funciones, tenemos que especificar el nombre del módulo
y el nombre de la función, separados por un punto. Este formato se denomina
notación punto.
>>> grados = 45
>>> angulo = grados * 2 * math . pi / 360.0
>>> math . sin ( angulo )
La constante pi también hace parte del módulo matemático. Si usted recuerda geo-
metría puede verificar el resultado comparándolo con la raíz cuadrada de 2 divi-
dida por 2:
4.5 Composición 43
4.5. Composición
Así como las funciones matemáticas, las funciones de Python pueden componer-
se, de forma que una expresión sea parte de otra. Por ejemplo, usted puede usar
cualquier expresión como argumento a una función:
Esta sentencia toma el valor de pi, lo divide por 2, y suma este resultado al valor
de angulo. Después, la suma se le pasa como argumento a la función coseno (cos).
También se puede tomar el resultado de una función y pasarlo como argumento a
otra:
Usted puede inventar los nombres que desee para sus funciones con tal de que no
use una palabra reservada. La lista de parámetros especifica que información, si es
que la hay, se debe proporcionar a fin de usar la nueva función.
Se puede incluir cualquier número de sentencias dentro de la función, pero tienen
que sangrarse o indentarse a partir de la margen izquierda. En los ejemplos de este
libro usaremos un sangrado de dos espacios.
Las primeras funciones que vamos a escribir no tienen parámetros, así que la sin-
taxis luce así:
Esta función se llama nuevaLinea. Los paréntesis vacíos indican que no tiene pa-
rámetros. Contiene solamente una sentencia, que produce como salida una línea
vacía. Eso es lo que ocurre cuando se usa el comando print sin argumentos.
La sintaxis para llamar la nueva función es la misma que para las funciones prede-
finidas en Python:
Primera Linea.
Segunda Linea.
Note el espacio extra entre las dos líneas. ¿Qué pasa si deseamos más espacio entre
las líneas? Podemos llamar la misma función repetidamente:
O podemos escribir una nueva función llamada tresLineas que imprima tres lí-
neas:
4.7 Definiciones y uso 45
Esta función contiene tres sentencias, y todas están sangradas por dos espacios.
Como la próxima sentencia (print “Primera Linea) no está sangrada, Python la
interpreta afuera de la función.
Hay que enfatizar dos hechos sobre este programa:
2. Usted puede llamar una función dentro de otra función; en este caso tresLineas
llama a nuevaLinea.
Hasta este punto, puede que no parezca claro porque hay que tomarse la moles-
tia de crear todas estas funciones. De hecho, hay muchas razones, y este ejemplo
muestra dos:
llamada, ejecuta todas las sentencias internas, y regresa para continuar donde es-
taba previamente.
Esto suena sencillo, hasta que tenemos en cuenta que una función puede llamar
a otra. Mientras se está ejecutando una función, el programa puede ejecutar las
sentencias en otra función. Pero, mientras se está ejecutando la nueva función, ¡el
programa puede tener que ejecutar otra función!.
Afortunadamente, Python lleva la pista de donde está fielmente, así que cada vez
que una función termina, el programa continúa su ejecución en el punto donde se
la llamó. Cuando llega al fin del programa, la ejecución termina.
¿Cual es la moraleja de esta sórdida historia? Cuando lea un programa, no lo haga
de arriba hacia abajo. En lugar de ésto, siga el flujo de ejecución.
Spam Spam
>>> imprimaDoble (5)
5 5
>>> imprimaDoble (3.14159)
3.14159 3.14159
Observe algo muy importante, el nombre de la variable que pasamos como argu-
mento (m) no tiene nada que ver con el nombre del parámetro (pedro). No importa
como se nombraba el valor originalmente (en el lugar donde se hace el llamado);
en la función imprimaDoble, la seguimos llamando de la misma manera pedro.
Esta función toma dos argumentos, los concatena, y luego imprime el resultado
dos veces. Podemos llamar a la función con dos cadenas:
Los parámetros también son locales. Por ejemplo, afuera de la función imprimaDoble,
no existe algo como pedro. Si usted intenta usarlo Python se quejará.
Para llevar pista de los lugares en que pueden usarse las variables es útil dibujar un
diagrama de pila. Como los diagramas de estados, los diagramas de pila muestran
el valor de cada variable y además muestran a que función pertenece cada una.
Cada función se representa por un marco. Un marco es una caja con el nombre de
una función al lado y los parámetros y variables adentro. El diagrama de pila para
el ejemplo anterior luce así:
50 Funciones
Esta lista de funciones se denomina un trazado inverso. Nos informa en qué archi-
vo de programa ocurrió el error, en qué línea, y qué funciones se estaban ejecutan-
do en ese momento. También muestra la línea de código que causó el error.
4.12 Funciones con resultados 51
1. ¿Qué pasa si usted llama a una función y no hace nada con el resultado (no lo
asigna a una variable o no lo usa como parte de una expresión mas grande)?
2. ¿Qué pasa si usted usa una función sin un resultado como parte de una ex-
presión, tal como nuevaLinea() + 7?
4.13. Glosario
Llamada a función: sentencia que ejecuta una función. Consiste en el nombre de
la función seguido por una lista de argumentos encerrados entre paréntesis.
Notación punto: sintaxis para llamar una función que se encuentra en otro módu-
lo, especificando el nombre módulo seguido por un punto y el nombre de la
función (sin dejar espacios intermedios).
Función: es la secuencia de sentencias que ejecuta alguna operación útil y que tie-
ne un nombre definido. Las funciones pueden tomar o no tomar parámetros
y pueden entregar o no entregar un resultado.
Parámetro: nombre usado dentro de una función para referirse al valor que se
pasa como argumento.
Variable local: variable definida dentro de una función. Una variable local solo
puede usarse dentro de su función.
Trazado inverso: lista de las funciones que se estaban ejecutando y que se impri-
me cuando ocurre un error en tiempo de ejecución.
4.14. Ejercicios
1. Con un editor de texto cree un guión de Python que se llame pruebame3.py.
Escriba en este archivo una función que se llame nueveLineas que use la
función tresLineas para mostrar nueve líneas en blanco. Enseguida agre-
gue una función que se llame limpiaPantalla que muestre veinticinco lí-
neas en blanco. La última instrucción en su programa debe ser una llamada
a limpiaPantalla.
4.14 Ejercicios 53
3. Escriba una función que imprima la distancia que hay entre dos puntos ubi-
cados sobre el eje X de un plano cartesiano conociendo sus coordenadas ho-
rizontales.
4. Escriba una función que imprima la distancia que hay entre dos puntos ubi-
cados sobre el eje Y de un plano cartesiano conociendo sus coordenadas ver-
ticales.
5. Escriba una función que imprima la distancia que hay entre dos puntos en
un plano coordenado, recordando el teorema de Pitágoras.
Condicionales y recursión
>>> cociente = 7 / 3
>>> print cociente
2
>>> residuo = 7 % 3
>>> print residuo
1
55
56 Condicionales y recursión
>>> 5 == 5
True
>>> 5 == 6
False
En la primera sentencia, los dos operandos son iguales, así que la expresión evalúa
a True (cierto); en la segunda sentencia, 5 no es igual a 6, así que obtenemos False
(falso).
El operador == es uno de los operadores de comparación; los otros son:
x != y # x no es igual y
x > y # x es mayor que y
x < y # x es menor que y
x >= y # x es mayor o igual a y
x <= y # x es menor o igual a y
Aunque estas operaciones probablemente son familiares para usted, los símbolos
en Python difieren de los matemáticos. Un error común consiste en usar un solo
signo igual (=) en lugar en un doble signo igual (==). Recuerde que = es el operador
para la asignación y que == es el operador para comparación. Tenga en cuenta que
no existen los signos =< o =>.
>>> x = 5
>>> x and 1
1
>>> y = 0
>>> y and 1
0
if x > 0:
print " x es positivo "
CABECERA:
PRIMERA SENTENCIA
...
ULTIMA SENTENCIA
58 Condicionales y recursión
La cabecera comienza en una nueva línea y termina con dos puntos seguidos (:).
Las sentencias sangradas o indentadas que vienen a continuación se denominan
el bloque. La primera sentencia sin sangrar marca el fin del bloque. Un bloque de
sentencias dentro de una sentencia compuesta también se denomina el cuerpo de
la sentencia.
No hay límite en el número de sentencias que pueden aparecer en el cuerpo de
una sentencia, pero siempre tiene que haber, al menos, una. Ocasionalmente, es
útil tener un cuerpo sin sentencias (como un hueco para código que aún no se ha
escrito). En ese caso se puede usar la sentencia pass, que no hace nada.
if x %2 == 0:
print x , " es par "
else :
print x , " es impar "
def imprimirParidad ( x ):
if x %2 == 0:
print x , " es par "
else :
print x , " es impar "
mento.
Algunas veces hay más de dos posibilidades y necesitamos más de dos ramas. Una
forma de expresar un cálculo así es un condicional encadenado:
if x < y :
print x , " es menor que " , y
elif x > y :
print x , " es mayor que " , y
else :
print x , " y " , y , " son iguales "
elif es una abreviatura de “else if.” De nuevo, exactamente una de las ramas se
ejecutará. No hay límite en el número de sentencias elif, pero la última rama tiene
que ser una sentencia else:
import math
5.9. Recursión
Hemos mencionado que es legal que una función llame a otra, y usted ha visto
varios ejemplos así. Hemos olvidado mencionar el hecho de que una función tam-
bién puede llamarse a sí misma. Al principio no parece algo útil, pero resulta ser
una de las capacidades más interesantes y mágicas que un programa puede tener.
Por ejemplo, observe la siguiente función:
62 Condicionales y recursión
def conteo ( n ):
if n == 0:
print " Despegue ! "
else :
print n
conteo (n -1)
3
2
1
Despegue!
5.10 Diagramas de pila para funciones recursivas 63
Este trabajo no sería de mucha ayuda si quisiéramos desplegar 2 líneas o 106. Una
mejor alternativa sería:
def nLineas ( n ):
if n > 0:
print
nLineas (n -1)
Esta función es similar a conteo; en tanto n sea mayor a 0, despliega una nueva
línea y luego se llama a sí misma para desplegar n-1 líneas adicionales. Así, el
número total de nuevas líneas es 1 + (n - 1) que, si usted verifica con álgebra,
resulta ser n.
El proceso por el cual una función se llama a sí misma es la recursión, y se dice
que estas funciones son recursivas.
__main__
conteo n 3
conteo n 2
conteo n 1
conteo n 0
Como siempre, el tope de la pila es el marco para __main__. Está vacío porque no
creamos ninguna variable en __main__ ni le pasamos parámetros.
Los cuatro marcos de conteo tienen diferentes valores para el parámetro n. El fon-
do de la pila, donde n=0, se denomina el caso base . Como no hace una llamada
recursiva, no hay mas marcos.
Este trazado inverso es un poco más grande que el que vimos en el capítulo ante-
rior. Cuando se presenta el error, ¡hay más de 100 marcos de recurrir en la pila!.
Antes de llamar a raw_input es una muy buena idea desplegar un mensaje di-
ciéndole al usuario qué digitar. Este mensaje se denomina indicador de entrada
(prompt en inglés). Podemos dar un argumento prompt a raw_input:
Para evitar este error, es una buena idea usar raw_input para obtener una cadena
y las funciones de conversión para transformarla en otros tipos.
5.13. Glosario
Operador residuo: operador que se denota con un signo porcentaje ( %), y trabaja
sobre enteros produciendo el residuo de un número al dividirlo por otro.
Operador de comparación: uno de los operadores que compara dos valores: ==,
!=, >, <, >=, y <=.
Operador lógico: uno de los operadores que combina expresiones booleanas: and,
or, y not.
Anidamiento: situación en la que hay una estructura dentro de otra, tal como una
sentencia condicional dentro de una rama de otra sentencia condicional.
Caso base: corresponde a una rama de la sentencia condicional dentro de una fun-
ción recursiva, que no hace un llamado recursivo.
5.14 Ejercicios 67
Prompt (indicador de entrada): una pista visual que le indica al usuario que digi-
te alguna información.
5.14. Ejercicios
1. Evalúe la expresión 7 % 0. Explique lo que ocurre.
2. Envuelva el código que viene a continuación en una función llamada comparar(x, y).
Llame a la función comparar tres veces: una en la que el primer argumento
sea menor que el segundo, otra en la que aquel sea mayor que éste, y una
tercera en la que los argumentos sean iguales.
if x < y :
print x , " es menor que " , y
elif x > y :
print x , " es mayor que " , y
else :
print x , " y " , y , " son iguales "
a) "not(p or q)"
b) "p and q"
c) "not(p and q)"
d) "not(p) or not(q)"
e) "not(p) and not(q)"
Funciones fructíferas
Pero hasta ahora ninguna de las funciones que hemos escrito ha retornado un va-
lor.
En este capítulo vamos a escribir funciones que retornan valores, los cuales deno-
minamos funciones fructíferas, o provechosas1 . El primer ejemplo es area, que
retorna el área de un círculo dado su radio:
import math
nominan procedimientos y las que veremos en este capítulo sí se denominan funciones, ya que los len-
guajes de programación usados para enseñar (como Pascal) hacían la distinción. Muchos lenguajes de
programación vigentes (incluido Python y C) no diferencian sintácticamente entre procedimientos y
funciones, por eso usamos esta terminología
69
70 Funciones fructíferas
return temp
Ya nos habíamos topado con la sentencia return antes, pero, en una función fructí-
fera, la sentencia return incluye un valor de retorno. Esta sentencia significa: “Re-
torne inmediatamente de esta función y use la siguiente expresión como un valor
de retorno.” La expresión proporcionada puede ser arbitrariamente compleja, así
que podríamos escribir esta función más concisamente:
def area ( radio ):
return math . pi * radio **2
Por otro lado, las variables temporales, como temp, a menudo permiten depurar
los programas más fácilmente.
Algunas veces es muy útil tener múltiples sentencias return, ubicadas en ramas
distintas de un condicional:
def valorAbsoluto ( x ):
if x < 0:
return -x
else :
return x
Ya que estas sentencias return están en un condicional alternativo, sólo una será
ejecutada. Tan pronto como esto suceda, la función termina sin ejecutar las senten-
cias que siguen.
El código que aparece después de la sentencia return, o en un lugar que el flujo
de ejecución nunca puede alcanzar, se denomina código muerto.
En una función fructífera es una buena idea garantizar que toda ruta posible de
ejecución del programa llegue a una sentencia return. Por ejemplo:
def valorAbsoluto ( x ):
if x < 0:
return -x
elif x > 0:
return x
Este programa no es correcto porque si x llega a ser 0, ninguna condición es cierta y
la función puede terminar sin alcanzar una sentencia return. En este caso el valor
de retorno que Python entrega es un valor especial denominado None:
6.2 Desarrollo de programas 71
p
distancia = (x2 − x1 )2 + (y2 − y1 )2 (6.1)
def distancia ( x1 , y1 , x2 , y2 ):
return 0.0
>>> distancia (1 , 2 , 4 , 6)
0.0
def distancia ( x1 , y1 , x2 , y2 ):
dx = x2 - x1
dy = y2 - y1
print " dx es " , dx
print " dy es " , dy
return 0.0
Si la función trabaja bien, las salidas deben ser 3 y 4. Si es así, sabemos que la
función está obteniendo los parámetros correctos y calculando el primer paso co-
rrectamente. Si no ocurre ésto, entonces hay unas pocas líneas para chequear.
Ahora calculamos la suma de los cuadrados de dx y dy:
def distancia ( x1 , y1 , x2 , y2 ):
dx = x2 - x1
dy = y2 - y1
discuadrado = dx **2 + dy **2
print " discuadrado es : " , discuadrado
return 0.0
Note que hemos eliminado las sentencias print que teníamos en el paso anterior.
Este código se denomina andamiaje porque es útil para construir el programa pero
no hace parte del producto final.
6.3 Composición 73
def distancia ( x1 , y1 , x2 , y2 ):
dx = x2 - x1
dy = y2 - y1
discuadrado = dx **2 + dy **2
resultado = math . sqrt ( discuadrado )
return resultado
3. Ya que el programa esté corriendo, usted puede remover parte del andamiaje
o consolidar múltiples sentencias en expresiones compuestas, pero sólo si
ésto no dificulta la lectura del programa.
6.3. Composición
Como usted esperaría, se puede llamar una función fructífera desde otra. Esta ca-
pacidad es la composición.
Como ejemplo vamos a escribir una función que toma dos puntos: el centro de un
círculo y un punto en el perímetro, y que calcule el área total del círculo.
74 Funciones fructíferas
Asuma que el punto central está almacenado en las variables xc y yc, y que el pun-
to perimetral está en xp y yp. El primer paso es encontrar el radio del círculo, que es
la distancia entre los dos puntos. Afortunadamente, hay una función, distancia,
que hace eso:
radio = distancia ( xc , yc , xp , yp )
El segundo paso es encontrar el área de un círculo con dicho radio y retornarla:
resultado = area ( radio )
return resultado
Envolviendo todo en una función obtenemos:
def area2 ( xc , yc , xp , yp ):
radio = distancia ( xc , yc , xp , yp )
resultado = area ( radio )
return resultado
Llamamos a esta función area2 para distinguirla de la función area definida pre-
viamente. Solo puede haber una función con un nombre dado dentro de un módu-
lo.
Las variables temporales radio y area son útiles para desarrollar y depurar, pero
una vez que el programa está funcionando podemos hacer la función más concisa
componiendo las llamadas a funciones:
def area2 ( xc , yc , xp , yp ):
return area ( distancia ( xc , yc , xp , yp ))
def esDivisible (x , y ):
return x % y == 0
>>> esDivisible (6 , 4)
False
>>> esDivisible (6 , 3)
True
if esDivisible (x , y ):
print " x es divisible por y "
else :
print " x no es divisible por y "
if esDivisible (x , y ) == True :
(de hecho, necesitaría algunos comandos mas para manejar dispositivos como el
teclado, el ratón, los discos, etc., pero eso sería todo).
Demostrar esta afirmación no es un ejercicio trivial y fue logrado por Alan Turing,
uno de los primeros científicos de la computación (algunos dirían que el era un
matemático, pero la mayoría de los científicos pioneros de la computación eran
matemáticos). Esto se conoce como la Tesis de Turing. Si usted toma un curso de
Teoría de la Computación tendrá la oportunidad de ver la demostración.
Para darle una idea de lo que puede hacer con las herramientas que ha aprendido,
vamos a evaluar unas pocas funciones matemáticas definidas recursivamente.
Una definición recursiva es similar a una circular, ya que éstas contienen una refe-
rencia al concepto que se pretende definir. Una definición circular verdadera no es
muy útil:
def factorial ( n ):
def factorial ( n ):
if n == 0:
return 1
6.5 Más recursión 77
Sino, y ésta es la parte interesante, tenemos que hacer una llamada recursiva para
encontrar el factorial de n − 1 y, entonces, multiplicarlo por n:
def factorial ( n ):
if n == 0:
return 1
else :
recur = factorial (n -1)
da = n * recur
return da
__main__
6
factorial n 3 recur 2 da 6
2
factorial n 2 recur 1 da 2
1
factorial n 1 recur 1 da 1
1
factorial n 0
Los valores de retorno mostrados se pasan hacia arriba a través de la pila. En cada
marco, el valor de retorno es el valor de da, que es el producto de n y recur.
Observe que en el último marco, las variables locales recur y da no existen porque
la rama que las crea no se ejecutó.
6.6. El salto de fe
Seguir el flujo de ejecución es una forma de leer programas, pero rápidamente
puede tornarse algo laberíntico. Una alternativa es lo que denominamos hacer el
“salto de fe.” Cuando usted llega a un llamado de función, en lugar de seguir el
flujo de ejecución, se asume que la función trabaja correctamente y retorna el valor
apropiado.
De hecho, usted ya está haciendo el salto de fe cuando usa las funciones primitivas.
Cuando llama a math.cos ó a math.exp, no está examinando las implementaciones
de estas funciones. Usted sólo asume que están correctas porque los que escribie-
ron el módulo math son buenos programadores.
Lo mismo se cumple para una de sus propias funciones. Por ejemplo, en la Sec-
ción 6.4, escribimos una función llamada esDivisible que determina si un núme-
ro es divisible por otro. Una vez que nos hemos convencido de que esta función
es correcta —probándola y examinando el código—podemos usarla sin mirar el
código nuevamente.
Lo mismo vale para los programas recursivos. Cuando usted llega a una llamada
6.7 Un ejemplo más 79
f ibonacci(0) = 1
f ibonacci(1) = 1
f ibonacci(n) = f ibonacci(n − 1) + f ibonacci(n − 2);
Si usted intenta seguir el flujo de ejecución de fibonacci, incluso para valores pe-
queños de n, le va a doler la cabeza. Pero, si seguimos el salto de fe, si asumimos
que los dos llamados recursivos funcionan correctamente, es claro que el resultado
correcto es la suma de éstos dos.
Parece recursión infinita. ¿Cómo puede darse? Hay un caso base —cuando n ==
0. El problema reside en que los valores de n se saltan al caso base .
En la primera llamada recursiva el valor de n es 0.5. En la siguiente es -0.5. Desde
allí se hace cada vez más pequeño, pero nunca será 0.
Tenemos dos opciones, podemos intentar generalizar la función factorial para
que trabaje con números de punto flotante, o podemos chequear el tipo del pará-
metro que llega. La primera opción se denomina en matemática la función gama y
está fuera del alcance de este libro. Optaremos por la segunda.
Podemos usar la función type para comparar el tipo del parámetro al tipo de un
valor entero conocido (como 1). Mientras estamos en eso también aseguraremos
que el parámetro sea positivo:
def factorial ( n ):
if type ( n ) != type (1):
print " Factorial solo esta definido para enteros . "
return -1
elif n < 0:
print " Factorial solo esta definido para positivos "
return -1
elif n == 0:
return 1
else :
return n * factorial (n -1)
6.9 Pruebas unitarias con doctest 81
Ahora tenemos tres casos base. El primero atrapa a los valores que no son ente-
ros, el segundo a los enteros negativos. En ambos casos el programa imprime un
mensaje de error y retorna un valor especial, -1, para indicar que algo falló:
radio : número
Pruebas :
>>> area (1)
1
"""
return lado **2
Logramos obtener una función que se puede probar en un caso particular. El mó-
dulo doctest de Python permite ejecutar automáticamente los casos de prueba que
tengamos en las funciones agregando al final del guión su importación y el lla-
mado de la función testmod(), como se ilustra a continuación con la función area,
ahora con cuatro casos de prueba:
"""
return lado **2
Si se ejecuta el guión se ejecutarán todas las pruebas unitarias de todas las funcio-
nes, esto nos permite atrapar errores rápidamente y corregirlos. En Unix/Linux, al
6.10 Glosario 83
6.10. Glosario
Función fructífera: función que retorna un resultado.
Código muerto: parte de un programa que nunca puede ser ejecutada, a menudo
porque aparece después de una sentencia return.
None: valor especial en Python retornado por las funciones que no tienen una
sentencia return, o que tienen una sentencia return sin un argumento.
Guarda: una condición que chequea y controla circunstancias que pueden causar
errores.
6.11. Ejercicios
1. Escriba la función comparar(a,b) que devuelva 1 si a < b, 0 si a = b, y -1 si
a>b
Iteración
pedro = 5
print pedro ,
pedro = 7
print pedro
Con asignación múltiple es muy importante distinguir entre una asignación y una
igualdad. Como Python usa el signo igual (=) para la asignación podemos caer en
la tentación de interpretar a una sentencia como a = b como si fuera una igualdad.
85
86 Iteración
¡Y no lo es!
Primero, la igualdad es conmutativa y la asignación no lo es. Por ejemplo, en la
matemática si a = 7 entonces 7 = a. Pero en Python, la sentencia a = 7 es legal
aunque 7 = a no lo es.
Además, en matemática, una sentencia de igualdad siempre es cierta. Si a = b ahora,
entonces a siempre será igual a b. En Python, una sentencia de asignación puede
lograr que dos variables sean iguales pero sólo por un tiempo determinado:
a = 5
b = a # a y b ahora son iguales
a = 3 # a y b no son iguales ahora
def conteo ( n ):
while n > 0:
print n
n = n -1
print " Despegue ! "
La sentencia while se puede leer como en el lenguaje natural. Quiere decir, “Mien-
tras n sea mayor que 0, continúe desplegando el valor de n y reduciendo el valor
de n en 1. Cuando llegue a 0, despliegue la cadena Despegue!”.
Más formalmente, el flujo de ejecución de una sentencia while luce así:
El cuerpo comprende todas las sentencias bajo la cabecera que tienen la misma
indentación.
Este flujo se denomina ciclo porque el tercer paso da la vuelta hacia el primero.
Note que si la condición es falsa la primera vez que se entra al while, las sentencias
internas nunca se ejecutan.
El cuerpo del ciclo debería cambiar el valor de una o más variables, de forma que la
condición se haga falsa en algún momento y el ciclo termine. De otra forma, el ciclo
se repetirá para siempre, obteniendo un ciclo infinito. Una broma común entre
los científicos de la computación es interpretar las instrucciones de los champús,
“Aplique champú, aplique rinse, repita,” como un ciclo infinito.
En el caso de conteo, podemos probar que el ciclo termina porque sabemos que el
valor de n es finito, y podemos ver que va haciéndose más pequeño cada vez que
el while itera (da la vuelta), así que eventualmente llegaremos a 0. En otros casos
esto no es tan fácil de asegurar:
def secuencia ( n ):
while n != 1:
print n ,
if n %2 == 0: # n es par
n = n /2
else : # n es impar
n = n *3+1
88 Iteración
La condición para este ciclo es n != 1, así que se repetirá hasta que n sea 1, lo que
hará que la condición sea falsa.
En cada iteración del ciclo while el programa despliega el valor de n y luego che-
quea si es par o impar. Si es par, el valor de n se divide por 2. Si es impar el valor
se reemplaza por n*3+1. Si el valor inicial (del argumento) es 3, la secuencia que
resulta es 3, 10, 5, 16, 8, 4, 2, 1.
Como n aumenta algunas veces y otras disminuye, no hay una demostración obvia
de que n llegará a ser 1, o de que el programa termina. Para algunos valores par-
ticulares de n podemos demostrar la terminación. Por ejemplo, si el valor inicial
es una potencia de dos, entonces el valor de n será par en cada iteración del ciclo
hasta llegar a 1. El ejemplo anterior termina con una secuencia así que empieza con
16.
Dejando los valores particulares de lado, la interesante pregunta que nos plantea-
mos es si podemos demostrar que este programa termina para todos los valores de
n. Hasta ahora, ¡nadie ha sido capaz de probarlo o refutarlo!.
7.3. Tablas
Una gama de aplicaciones, donde los ciclos se destacan, es la generación de in-
formación tabular. Antes de que los computadores existieran la gente tenía que
calcular logaritmos, senos, cosenos y otras funciones matemáticas a mano. Para
facilitar la tarea, los libros matemáticos incluían largas tablas con los valores de di-
chas funciones. La creación de las tablas era un proceso lento y aburridor, y tendían
a quedar con muchos errores.
Cuando los computadores entraron en escena, una de las reacciones iniciales fue
“Esto es maravilloso! Podemos usar los computadores para generar las tablas, de
forma que no habrían errores”. Eso resultó (casi) cierto, pero poco prospectivo.
Poco después los computadores y las calculadoras se hicieron tan ubicuos que las
tablas se hicieron obsoletas.
Bueno, casi. Para algunas operaciones los computadores usan tablas de valores pa-
ra obtener una respuesta aproximada y luego hacer mas cálculos para mejorar la
aproximación. En algunos casos, se han encontrado errores en las tablas subyacen-
tes, el más famoso ha sido el de la tabla para realizar la división en punto flotante
7.3 Tablas 89
import math
x = 1.0
while x < 10.0:
print x , '\ t ' , math . log ( x )
x = x + 1.0
1.0 0.0
2.0 0.69314718056
3.0 1.09861228867
4.0 1.38629436112
5.0 1.60943791243
6.0 1.79175946923
7.0 1.94591014906
8.0 2.07944154168
9.0 2.19722457734
Si estos valores parecen extraños, recuerde que la función log usa la base e. Ya que
las potencias de dos son importantes en la ciencias de la computación, a menudo
deseamos calcular logaritmos en base 2. Para este fin podemos usar la siguiente
fórmula:
loge x
log2 x = (7.1)
loge 2
90 Iteración
resulta en:
1.0 0.0
2.0 1.0
3.0 1.58496250072
4.0 2.0
5.0 2.32192809489
6.0 2.58496250072
7.0 2.80735492206
8.0 3.0
9.0 3.16992500144
Podemos ver que 1, 2, 4, y 8 son potencias de dos porque sus logaritmos en base
2 son números enteros. Si deseamos calcular el logaritmo de más potencias de dos
podemos modificar el programa así:
x = 1.0
while x < 100.0:
print x , '\ t ' , math . log ( x )/ math . log (2.0)
x = x * 2.0
Ahora, en lugar de agregar algo a x en cada iteración del ciclo, produciendo una
serie aritmética, multiplicamos a x por algo constante, produciendo una serie geo-
métrica. El resultado es:
1.0 0.0
2.0 1.0
4.0 2.0
8.0 3.0
16.0 4.0
32.0 5.0
64.0 6.0
Puede que las tablas de logaritmos no sirvan en nuestros días, ¡pero para los cien-
tíficos de la computación saber las potencias de dos sí es muy importante!.
El carácter diagonal invertido (backslash) ’\’ indica el comienzo de una secuencia
de escape. Estas secuencias se utilizan para representar caracteres invisibles como
tabuladores y nuevas líneas. La secuencia ’\n’ representa una nueva línea.
Una secuencia de escape puede empezar en cualquier posición de una cadena; en
el ejemplo anterior, la secuencia de escape tabuladora es toda la cadena.
¿Cómo cree que se representa un diagonal invertido en una cadena?
i = 1
while i <= 6:
print 2* i , ' ',
i = i + 1
print
La primera línea inicializa una variable llamada i, que actúa como un contador o
variable de ciclo. A medida que se ejecuta el ciclo, el valor de i se incrementa de
1 a 6. Cuando i es 7, el ciclo termina. En cada iteración del ciclo despliega el valor
2*i, seguido de tres espacios.
De nuevo, la coma en la sentencia print suprime la nueva línea. Después de que
el ciclo termina la segunda sentencia print comienza una nueva línea.
La salida del programa es:
2 4 6 8 10 12
3 6 9 12 15 18
4 8 12 16 20 24
1 2 3 4 5 6
2 4 6 8 10 12
3 6 9 12 15 18
4 8 12 16 20 24
5 10 15 20 25 30
6 12 18 24 30 36
def i m p r i m i r T a b l a M u l t i p l i c a c i o n ():
i = 1
while i <= 6:
imprimaMultiplos ( i )
i = i + 1
imprimaMultiplos 1
n 3 i 2
1 2 3 4 5 6
2 4 6 8 10 12
3 6 9 12 15 18
4 8 12 16 20 24
5 10 15 20 25 30
6 12 18 24 30 36
7 14 21 28 35 42
Esto está bien, pero quizás deseamos que la tabla sea cuadrada—con el mismo
número de filas y columnas. Para lograrlo, añadimos un parámetro a imprima-
Multiplos que especifique cuántas columnas debe tener la tabla.
Sólo por confundir, vamos a nombrarlo tope, demostrando que diferentes funcio-
nes pueden tener parámetros con el mismo nombre (igual que las variables loca-
les). Aquí está el programa completo:
Note que cuando agregamos el nuevo parámetro tuvimos que cambiar la primera
línea de la función (la cabecera), y también el lugar donde la función se llama en
imprimaTabla.
Como se esperaba, este programa genera una tabla cuadrada de siete-por-siete:
1 2 3 4 5 6 7
2 4 6 8 10 12 14
3 6 9 12 15 18 21
4 8 12 16 20 24 28
5 10 15 20 25 30 35
6 12 18 24 30 36 42
7 14 21 28 35 42 49
imprimaMultiplos(i, tope)
por
imprimaMultiplos(i, i)
y se obtiene:
1
2 4
3 6 9
4 8 12 16
5 10 15 20 25
6 12 18 24 30 36
7 14 21 28 35 42 49
7.9 Funciones 97
7.9. Funciones
Ya hemos mencionado los “beneficios de las funciones.” Usted se estará pregunta-
do cuales son estos beneficios. Aquí hay algunos:
Las funciones bien diseñadas resultan ser útiles para muchos programas.
Una vez que usted escribe y depura una, se puede reutilizar.
7.10. Glosario
Asignación múltiple: realizar más de una asignación a una misma variable du-
rante la ejecución de un programa.
Ciclo: una sentencia o grupo de sentencias que se ejecuta repetidamente hasta que
una condición de terminación se cumple.
Tab: (tabulador) carácter especial que causa el movimiento del cursor al siguiente
punto de parada en la línea actual.
Nueva línea: carácter que causa que el cursor se mueva al principio de la siguiente
línea.
98 Iteración
Secuencia de escape: carácter de escape (\) seguido por uno o más caracteres im-
primibles que se usan para denotar un carácter no imprimible.
7.11. Ejercicios
1. Siga la ejecución de la última versión de imprimaTabla y descifre cómo tra-
baja.
produzca
esta
salida.
1 1
2 3
3 6
4 10
5 15
8. Compare los valores de n que obtuvo para los puntos 6 y 7. Explique si en-
cuentra diferencias.
9. Escriba una función, es_primo, que tome un solo argumento entero y devuel-
va True cuando el argumento es un número primo y False en caso contrario.
10. Agregue el código para chequeo de tipo de datos y para las prueba unitarias
a todas las funciones desarrolladas previamente.
100 Iteración
Capítulo 8
Cadenas
101
102 Cadenas
8.2. Longitud
La función len retorna el número de caracteres en una cadena:
Para acceder a la última letra de una cadena usted podría caer en algo como esto:
Alternativamente, podemos usar índices negativos, que cuentan hacia atrás des-
de el fin de la cadena. La expresión fruta[-1] retorna la última letra fruta[-2]
retorna la penúltima, y así sucesivamente.
núan hasta el final. Este patrón de procesamiento se denomina recorrido. Hay una
forma de realizarlo con la sentencia while:
indice = 0
while indice < len ( fruta ):
letra = fruta [ indice ]
print letra
indice = indice + 1
Este ciclo recorre la cadena y despliega cada letra en una línea independiente. La
condición del ciclo es indice < len(fruta), así que cuando indice se hace igual a
la longitud de la cadena, la condición es falsa, y el cuerpo del ciclo no se ejecuta. El
último carácter accedido es el que tiene el índice len(fruta)-1, es decir, el último.
Usar un índice para recorrer un conjunto de valores es tan común que Python tiene
una sintaxis alternativa más simple—el ciclo for :
Cada vez que el ciclo itera, el próximo carácter en la cadena se asigna a la variable
caracter. El ciclo continúa hasta que no quedan más caracteres.
El siguiente ejemplo muestra cómo usar la concatenación y un ciclo for para gene-
rar una serie en orden lexicográfico. Lexicográfico se refiere a una lista en la que los
elementos aparecen en orden alfabético. Por ejemplo, en el libro Make Way for Duc-
klings de Robert McCloskey, los nombres de los patos son Jack, Kack, Lack, Mack,
Nack, Ouack, Pack, and Quack. Este ciclo los despliega en orden:
Jack
Kack
Lack
104 Cadenas
Mack
Nack
Oack
Pack
Qack
Por supuesto que hay un error, ya que “Ouack” y “Quack” no están bien deletrea-
dos.
fruta "banano"
indice 0 1 2 3 4 5 6
Si usted omite el primer índice (antes de los puntos seguidos), el segmento comien-
za en el inicio de la cadena. Si se omite el segundo índice, el segmento va hasta el
final. Entonces:
El operador de comparación funciona con cadenas. Para ver si dos cadenas son
iguales:
Las otras operaciones de comparación son útiles para poner las palabras en orden
alfabético:
Sin embargo, usted debe ser consciente de que Python no maneja las letras minús-
culas y mayúsculas de la misma forma en que lo hace la gente. Todas las letras
mayúsculas vienen antes de las minúsculas. Si palabra vale “Zebra” la salida sería:
De cierta manera buscar es el opuesto del operador []. En vez de tomar un índice
y extraer el carácter correspondiente, toma un carácter y encuentra el índice donde
éste se encuentra. Si no se encuentra el carácter en la cadena, la función retorna -1.
8.8 Iterando y contando 107
El módulo string incluye una función denominada find que hace lo mismo que
buscar. Para llamarla tenemos que especificar el nombre del módulo y de la fun-
ción, usando la notación punto.
108 Cadenas
Uno de los beneficios de los módulos es que ayudan a evitar colisiones entre los
nombres de las funciones primitivas y los nombres de las funciones creadas por
el programador. Si hubiéramos nombrado a nuestra función buscar con la pala-
bra inglesa find, podríamos usar la notación punto para especificar que queremos
llamar a la función find del módulo string, y no a la nuestra.
De hecho string.find es más general que buscar, también puede buscar subcade-
nas, no solo caracteres:
También tiene un argumento adicional que especifica el índice desde el que debe
empezar la búsqueda:
Podemos usar estas constantes y la función find para clasificar los caracteres. Por
ejemplo, si find(lowercase, c) retorna un valor distinto de -1, entonces c debe
ser una letra minúscula:
def esMinuscula ( c ):
return string . find ( string . lowercase , c ) != -1
def esMinuscula ( c ):
return c in string . lowercase
def esMinuscula ( c ):
return 'a ' <= c <= 'z '
Un carácter de los que pertenecen a whitespace mueve el cursor sin imprimir nada.
Crean un espacio en blanco que se puede evidenciar entre caracteres. La constan-
te string.whitespace contiene todos los caracteres que representan espacios en
blanco: espacio, tab (\t), y nueva línea (\n).
Hay otras funciones útiles en el módulo string, pero este libro no es un manual
de referencia. Para esto usted puede consultar la referencia de las bibliotecas de
Python (Python Library Reference). Además, hay un gran cúmulo de documentación
en el sitio web de Python www.python.org.
110 Cadenas
8.11. Glosario
Tipo de dato compuesto: un tipo de dato en el que los valores están compuestos
por componentes o elementos, que, a su vez, son valores.
Contador: una variable que se usa para contar algo, usualmente se inicializa en
cero y se incrementa posteriormente dentro de un ciclo.
Espacio en blanco: cualquiera de los caracteres que mueven el cursor sin impri-
mir nada visible. La constante string.whitespace contiene todos los carac-
teres que representan espacios en blanco.
8.12. Ejercicios
Para cada función, agregue chequeo de tipos y pruebas unitarias.
1. Escriba una función que tome una cadena como argumento y despliegue las
letras al revés, una por cada línea.
2. Modifique el programa de la sección 8.3 para corregir el error con los patos
Ouack y Quack.
8.12 Ejercicios 111
6. Discuta qué versión de esMinuscula cree que es la más rápida. ¿Puede pen-
sar en otra razón distinta de la velocidad para preferir alguna de ellas sobre
las otras?
def invertir ( s ):
"""
>>> invertir ( ' feliz ')
' zilef '
>>> invertir ( ' Python ')
' nohtyP '
>>> invertir ("")
''
>>> invertir (" P ")
'P '
"""
Agregue un cuerpo a la función invertir que haga que pase las pruebas uni-
tarias.
Agregue al archivo cadenas.py cuerpos a cada una de las siguientes funcio-
nes, una a la vez.
8. Reflejar:
112 Cadenas
def reflejar ( s ):
"""
>>> reflejar (" bien ")
' bienneib '
>>> reflejar (" sí ")
' síís '
>>> reflejar ( ' Python ')
' PythonnohtyP '
>>> reflejar ("")
''
>>> reflejar (" a ")
' aa '
"""
9. Eliminar letra:
10. Es palíndromo:
def es_palindromo ( s ):
"""
>>> es_palindromo ( ' abba ')
True
>>> es_palindromo ( ' abab ')
8.12 Ejercicios 113
False
>>> es_palindromo ( ' tenet ')
True
>>> es_palindromo ( ' banana ')
False
>>> es_palindromo ( ' zorra arroz ')
True
"""
Listas
[10 , 20 , 30 , 40]
[ " correo " , " lapiz " , " carro " ]
El primer ejemplo es una lista de cuatro enteros, la segunda, una lista de tres ca-
denas. Los elementos de una lista no tienen que tener el mismo tipo. La siguiente
lista contiene una cadena, un flotante, un entero y (mirabile dictu) otra lista:
Cuando una lista está contenida por otra se dice que está anidada.
Las listas que contienen enteros consecutivos son muy comunes, así que Python
proporciona una forma de crearlas:
115
116 Listas
La función range toma dos argumentos y retorna una lista que contiene todos los
enteros desde el primero hasta el segundo, ¡incluyendo el primero y no el último!
Hay otras formas de usar a range. Con un solo argumento crea una lista que em-
pieza en 0:
Si hay un tercer argumento, este especifica el espacio entre los valores sucesivos,
que se denomina el tamaño del paso. Este ejemplo cuenta de 1 a 10 con un paso de
tamaño 2:
>>> range (1 , 10 , 2)
[1 , 3 , 5 , 7 , 9]
Finalmente, existe una lista especial que no contiene elementos. Se denomina lista
vacía, y se denota con [].
Con todas estas formas de crear listas sería decepcionante si no pudiéramos asig-
nar listas a variables o pasarlas como parámetros a funciones. De hecho, podemos
hacerlo:
>>> vocabulario = [ " mejorar " , " castigar " , " derrocar " ]
>>> numeros = [17 , 123]
>>> vacia = []
>>> print vocabulario , numeros , vacia
[ " mejorar " , " castigar " , " derrocar " ] [17 , 123] []
El operador corchete para listas puede aparecer en cualquier lugar de una expre-
sión. Cuanto aparece al lado izquierdo de una asignación cambia uno de los ele-
mentos de la lista de forma que el elemento 1 de numeros, que tenía el valor 123,
ahora es 5.
Cualquier expresión entera puede usarse como índice:
Si usted intenta leer o escribir un elemento que no existe, obtiene un error en tiem-
po de ejecución:
Si el índice tiene un valor negativo, cuenta hacia atrás desde el final de la lista:
combatientes = [ " guerra " , " hambruna " , " peste " , " muerte " ]
i = 0
while i < 4:
print combatientes [ i ]
i = i + 1
118 Listas
combatientes = [ " guerra " , " hambruna " , " peste " , " muerte " ]
i = 0
while i < len ( combatientes ):
print combatientes [ i ]
i = i + 1
[ ' basura ! ' , 1 , [ ' Brie ' , ' Roquefort ' , ' Pol le Veq '] ,
[1 , 2 , 3]]
9.4. Pertenencia
in es un operador booleano que chequea la pertenencia de un valor a una secuen-
cia. Lo usamos en la Sección 8.10 con cadenas, pero también funciona con listas y
otras secuencias:
9.5 Listas y ciclos for 119
>>> combatientes = [ " guerra " , " hambruna " , " peste " , " muerte " ]
>>> ' peste ' in combatientes
True
>>> ' corrupcion ' in combatientes
False
Ya que “peste” es un miembro de la lista combatientes, el operador in retorna
cierto. Como “corrupcion” no está en la lista, in retorna falso.
Podemos usar el operador lógico not en combinación con el in para chequear si
un elemento no es miembro de una lista:
>>> ' corrupcion ' not in combatientes
True
for fruta in [ " banano " , " manzana " , " pera " ]:
print " Me gustaria comer " + fruta + " s ! "
El primer ejemplo imprime todos los números pares entre uno y diecinueve. El
segundo expresa entusiasmo sobre varias frutas.
>>> 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 [1, 2, 3] tres veces.
>>> lista = [ 'a ' , 'b ' , 'c ' , 'd ' , 'e ' , 'f ']
>>> lista [1:3]
[ 'b ' , 'c ']
9.8 Las listas son mutables 121
>>> fruta = [ " banano " , " manzana " , " pera " ]
>>> fruta [0] = " mandarina "
>>> fruta [ -1] = " naranja "
>>> print fruta
[ ' mandarina ' , ' manzana ' , ' naranja ']
>>> lista = [ 'a ' , 'b ' , 'c ' , 'd ' , 'e ' , 'f ']
>>> lista [1:3] = [ 'x ' , 'y ']
>>> print lista
[ 'a ' , 'x ' , 'y ' , 'd ' , 'e ' , 'f ']
>>> lista = [ 'a ' , 'b ' , 'c ' , 'd ' , 'e ' , 'f ']
>>> lista [1:3] = []
>>> print lista
[ 'a ' , 'd ' , 'e ' , 'f ']
>>> a = [ ' one ' , ' two ' , ' three ']
>>> del a [1]
>>> a
[ ' one ' , ' three ']
>>> lista = [ 'a ' , 'b ' , 'c ' , 'd ' , 'e ' , 'f ']
>>> del lista [1:5]
>>> print lista
[ 'a ' , 'f ']
Como de costumbre, los segmentos seleccionan todos los elementos hasta el se-
gundo índice, sin incluirlo.
La función append agrega un elemento (o una lista) al final de una lista existente:
sabemos que a y b se referirán a una cadena con las letras “banana”. Pero no pode-
mos afirmar que sea la misma cadena.
Hay dos situaciones posibles:
a "banana" a
"banana"
b "banana" b
>>> id ( a )
135044008
>>> id ( b )
135044008
De hecho, obtenemos el mismo identificador dos veces, lo que nos dice que Python
sólo creó una cadena, y que a y b se refieren a ella.
Las listas, por otro lado, se comportan de manera diferente. Cuando creamos dos
listas obtenemos dos objetos:
>>> a = [1 , 2 , 3]
>>> b = [1 , 2 , 3]
>>> id ( a )
135045528
>>> id ( b )
135041704
a [ 1, 2, 3 ]
b [ 1, 2, 3 ]
9.11. Alias
Como las variables se pueden referir a objetos, si asignamos una variable a otra,
las dos se referirán al mismo objeto:
>>> a = [1 , 2 , 3]
>>> b = a
En este caso el diagrama de estados luce así:
a
[ 1, 2, 3 ]
b
Como la misma lista tiene dos nombres distintos, a y b, podemos decir que b es un
alias de a. Los cambios que se hagan a través de un alias afectan al otro:
>>> b [0] = 5
>>> print a
[5 , 2 , 3]
Aunque este comportamiento puede ser útil, algunas veces puede ser indeseable.
En general, es más seguro evitar los alias cuando se está trabajando con objetos
mutables. Para objetos inmutables no hay problema. Esta es la razón por la que
Python tiene la libertad de crear alias a cadenas cuando ve la oportunidad de eco-
nomizar memoria. Pero tenga en cuenta que esto puede variar en las diferentes
versiones de Python; por lo tanto no es recomendable realizar programas que de-
pendan de este comportamiento.
__main__ numeros
[ 1, 2, 3 ]
cabeza lista
126 Listas
Como el objeto lista está compartido por dos marcos, lo dibujamos en el medio.
Si una función modifica un parámetro de tipo lista, el que hizo el llamado ve los
cambios. Por ejemplo, borrarCabeza borra el primer elemento de una lista:
>>> numeros = [1 , 2 , 3]
>>> borrarCabeza ( numeros )
>>> print numeros
[2 , 3]
Si una función retorna una lista, retorna una referencia a ella. Por ejemplo, la fun-
ción cola retorna una lista que contiene todos los elementos, excepto el primero:
>>> numeros = [1 , 2 , 3]
>>> resto = cola ( numeros )
>>> print resto
[2 , 3]
Como el valor de retorno se creó con el operador segmento, es una nueva lista. La
creación de resto, y los cambios subsecuentes sobre esta variable no tienen efecto
sobre numeros.
Las aplicaciones del operador corchete se evalúan de izquierda a derecha, así que
ésta expresión obtiene el elemento 3 de lista y extrae de allí el elemento 1.
9.15. Matrices
Las listas anidadas se usan a menudo para representar matrices. Por ejemplo, la
matriz:
1 2 3
4 5 6
7 8 9
se puede representar así:
matriz es una lista con tres elementos, cada uno es una fila. Podemos seleccionar
una fila de la manera usual:
>>> string . split ( " La rana que canta " , " an " )
[ ' La r ' , 'a que c ' , ' ta ']
>>> m = [ ' La ' , ' vida ' , ' es ' , ' un ' , ' ratico ']
>>> string . join ( m )
' La vida es un ratico '
Como split, join puede recibir un argumento adicional separador que se inserta
entre los elementos:
9.17. Glosario
Lista: colección de objetos que recibe un nombre. Cada objeto se identifica con un
índice o número entero positivo.
Índice: valor o variable entero que indica la posición de un elemento en una lista.
9.18 Ejercicios 129
Elemento: uno de los valores dentro de una lista (u otra secuencia). El operador
corchete selecciona elementos de una lista.
Clonar: crear un objeto con el mismo valor que un objeto preexistente. Copiar una
referencia a un objeto crea un alias, pero no clona el objeto.
Delimitador: carácter o cadena que se usa para indicar el lugar donde una cadena
debe ser separada.
9.18. Ejercicios
Para cada función, agregue chequeo de tipos y pruebas unitarias.
1. Escriba una función llamada medio que reciba una lista y retorne una nue-
va lista que contenga todos los elementos de la lista de entrada excepto el
primero y el último. Por ejemplo, medio([1,2,3,4]) debe retornar [2,3].
2. Escriba una función llamada cortar que reciba una lista y la modifique elimi-
nando el primer y el último elemento, retornando None.
3. Escriba una función que recorra una lista de cadenas imprimiendo la longi-
tud de cada una. ¿Qué pasa si usted le pasa un entero a len?
5. Escriba una función llamada esta_ordenada que tome una lista como pará-
metro y retorne True si la lista está ordenada de forma ascendente o False
si no lo está. Usted puede asumir como precondición que los elementos son
comparables con los operadores relacionales. Por ejemplo:
6. Dos palabras son anagramas si se pueden reordenar las letras de una palabra
para formar la otra. Escriba una función llamada es_anagrama que tome dos
cadenas y retorne True si son anagramas y False en caso contrario.
8. Escriba dos versiones de una función que lea el archivo palabras.txt y cons-
truya una lista con un elemento por palabra. Una versión usará el método
append y la otra la construcción t=t+[x]. ¿Cual es mas lenta? ¿Por qué? Pista:
use el módulo time para medir lo que tarda la ejecución de las versiones.
palabras.txt: http://cic.javerianacali.edu.co/abecerra/files/palabras.
txt
Solución: http://thinkpython.com/code/wordlist.py
Solución: http://thinkpython.com/code/birthday.py
10. Dos palabras son un “par inverso” si cada una es la inversa de la otra. Es-
criba un programa que encuentre todos los pares inversos del español (pala-
bras.txt).
Solución: http://thinkpython.com/code/reverse_pair.py
9.18 Ejercicios 131
11. Dos palabras se entretejen si tomando las letras de las dos, alternándose, se
puede formar una nueva palabra. Por ejemplo: ’pie’ y ’en’ se entretejen en
’peine’.
Solución: http://thinkpython.com/code/interlock.py
132 Listas
Capítulo 10
Interludio 1: Triqui
10.1. Motivación
Con el fin de poner en práctica los conceptos de los capítulos anteriores vamos
a desarrollar un sencillo juego de triqui para dos jugadores. La idea es seguir un
desarrollo iterativo que toma un pequeño programa y lo convierte, poco a poco,
en un juego de triqui con toda la funcionalidad esperada.
El código fuente tiene diez versiones, comenzando desde triqui0.py hasta triqui9.py.
Los diez programas puede descargarse de:
http://cic.javerianacali.edu.co/~abecerra/files/triqui.zip
Para comprender el capítulo hay que ejecutar cada versión del programa a me-
dida que se avanza en la lectura. En las secciones a continuación se discuten los
fragmentos del programa a medida que se van agregando, cada fragmento tiene el
nombre del archivo en que se introdujo como comentario inicial.
133
134 Interludio 1: Triqui
10.3. Inicio
Con la convención anterior, nuestro triqui empieza humildemente:
# triqui0 . py
def crear ():
m = [ [ ' ' , ' ' , ' '] ,
[ ' ' , ' ' , ' '] ,
[ ' ' , ' ' , ' '] ]
return m
triqui = crear ()
imprimir ( triqui )
Ahora podemos agregar un ciclo para jugar, sencillo, con un solo jugador:
10.4 ¿Quien gana? 135
# triqui1 . py
while True :
print " Juegue jugador O "
f = input ( " fila ? " )
c = input ( " columna ? " )
triqui [ f ][ c ] = " O "
imprimir ( triqui )
# triqui3 . py
def ganaDiagonal1 ( jugador , tablero ):
for i in range (3):
if tablero [ i ][ i ]!= jugador :
return False
return True
La idea es que si encuentra algo diferente del símbolo del jugador (’X’ ó ’O’), re-
torna False. Sino, retorna True. La otra diagonal requiere mas trabajo, usamos el
hecho de que tablero[i][2-i] va dando los elementos de la segunda diagonal
para i de 0 a 2. ¡Verifiquelo!
# triqui3 . py
def ganaDiagonal2 ( jugador , tablero ):
for i in range (3):
if tablero [ i ][2 - i ]!= jugador :
return False
return True
Falta llamar las funciones en el ciclo del juego, y si alguien gana, terminamos el
juego con la sentencia break. Por ejemplo, para el primer jugador:
136 Interludio 1: Triqui
# triqui3 . py
print " Juegue jugador O "
f = input ( " fila ? " )
c = input ( " columna ? " )
triqui [ f ][ c ] = " O "
imprimir ( triqui )
if ganaDiagonal1 ( " O " , triqui ) or ganaDiagonal2 ( " O " , triqui ):
print " Gana el jugador O !!!! "
break
Agregar las funciones para verificar si alguien gana por alguna fila es sencillo.
Seguimos la misma estructura de las diagonales, creando una función ganaFila,
que verifica si el jugador gana en una de las filas del tablero.
# triqui4 . py
def ganaFila ( fila , jugador , tablero ):
""" Chequea si el jugador gana en la fila dada """
for i in range (3):
if tablero [ fila ][ i ]!= jugador :
return False
return True
# triqui4 . py
Las funciones para chequear las columnas son muy parecidas. Para llamarlas mo-
dificamos el ciclo del juego. Por ejemplo, para el jugador ’X’:
# triqui4 . py
while True :
print " Juegue jugador X "
10.5 Reestructurando el código 137
# triqui5 . py
def jugar ( jugador , tablero ):
print " Juegue jugador " , jugador
f = input ( " fila ? " )
c = input ( " columna ? " )
tablero [ f ][ c ] = jugador
imprimir ( triqui )
diag = ganaDiagonal1 ( jugador , tablero ) or
ganaDiagonal2 ( jugador , tablero )
linea = ganaHorizontal ( jugador , tablero ) or
ganaVertical ( jugador , tablero )
return diag or linea
138 Interludio 1: Triqui
Con este cambio nuestro ciclo de juego es más pequeño, y el programa es más fácil
de mantener:
# triqui5 . py
while True :
if jugar ( " O " , triqui ):
print " Gana el jugador O !!!! "
break
if jugar ( " X " , triqui ):
print " Gana el jugador X !!!! "
break
10.6. Validaciones
Los usuarios pueden cometer errores, por esta razón los programas deben revisar
todos los datos que generan para ver si cumplen las condiciones para operar. El
código que revisa una condición o restricción de este tipo se llama validación.
En el triqui podemos agregar validación al juego. Tanto f como c,los valores que
el usuario digita para jugar en una fila y columna deben ser enteros en el intervalo
[0,2] para que podamos representarlos en la matriz de 3 filas y 3 columnas. Ade-
más, la casilla tablero[f][c] debe estar vacía para que una jugada nueva pueda
hacerse allí.
Estas validaciones pueden ponerse en un ciclo que le pida al jugador digitar los
valores para f y c varias veces, hasta que sean correctos:
# triqui6 . py
def valido ( n ):
return 0 <=n <=2
imprimir ( tablero )
diag = ganaDiagonal1 ( jugador , tablero ) or
ganaDiagonal2 ( jugador , tablero )
linea = ganaHorizontal ( jugador , tablero ) or
ganaVertical ( jugador , tablero )
return diag or linea
10.7. Empates
Ahora agregamos una función para chequear si hay empate entre los jugadores.
Esto sucede si el tablero está lleno, o sea que no hay ninguna casilla vacía (con el
carácter ’ ’):
# triqui7 . py
# triqui8 . py
while True :
if jugar ( " O " , triqui ):
print " Gana el jugador O !!!! "
break
140 Interludio 1: Triqui
if empate ( triqui ):
print " Empate !!! "
break
if jugar ( " X " , triqui ):
print " Gana el jugador X !!!! "
break
if empate ( triqui ):
print " Empate !!! "
break
# triqui8 . py
def jugar ( jugador , tablero ):
while True :
print " Juegue jugador " , jugador
f = input ( " fila ? " )
c = input ( " columna ? " )
if type ( f )== type ( c )== type (1) and valido ( f )
and valido ( c ) and tablero [ f ][ c ]== ' ':
tablero [ f ][ c ] = jugador
break
else :
print " Posición inválida ! "
imprimir ( triqui )
diag = ganaDiagonal1 ( jugador , tablero ) or
ganaDiagonal2 ( jugador , tablero )
linea = ganaHorizontal ( jugador , tablero ) or
ganaVertical ( jugador , tablero )
return diag or linea
10.8 Reestructurando más 141
# triqui9 . py
def gana ( jugador , tablero ):
""" Analiza si el jugador gana la partida """
diag = ganaDiagonal1 ( jugador , tablero ) or
ganaDiagonal2 ( jugador , tablero )
linea = ganaHorizontal ( jugador , tablero ) or
ganaVertical ( jugador , tablero )
return diag or linea
Con este cambio también ganamos algo: la verificación de quien gana el juego
puede hacerse con una sola función en otro programa, por ejemplo uno con una
interfaz gráfica de usuario, como el del capítulo 20.
Ahora, gana se llama en el ciclo principal. Y todo esto se puede poner en la parte
principal del programa:
# triqui9 . py
if __name__ == ' __main__ ':
triqui = crear ()
imprimir ( triqui )
while True :
jugar ( " O " , triqui )
if gana ( " O " , triqui ):
print " Gana el jugador O !!!! "
break
if empate ( triqui ):
print " Empate !!! "
break
jugar ( " X " , triqui )
if gana ( " X " , triqui ):
print " Gana el jugador X !!!! "
142 Interludio 1: Triqui
break
if empate ( triqui ):
print " Empate !!! "
break
Así terminamos con triqui9.py, un programa con 12 funciones y un ciclo de juego
que tiene en total 124 líneas de código, ¡pero empezó como una simple impresión
de una matriz vacía!
10.9. Resumen
triqui0.py: crea e imprime el tablero vacío
triqui9.py: crea una función ’gana’ para que ’jugar’ sea mas pequeña y pone el
ciclo del juego en la parte principal.
10.10. Glosario
validación: análisis de los datos que genera un usuario humano para que estén
dentro de los límites de operación del software.
10.11. Ejercicios
1. Modifique el triqui para que el computador juegue automáticamente, selec-
cionando una casilla vacía al azar.
Tuplas
>>> tupla = 'a ' , 'b ' , 'c ' , 'd ' , 'e '
>>> tupla = ( 'a ' , 'b ' , 'c ' , 'd ' , 'e ')
Para crear una tupla con un único elemento, tenemos que incluir la coma final:
145
146 Tuplas
Las operaciones sobre tuplas son las mismas que vimos con las listas. El operador
corchete selecciona un elemento de la tupla.
>>> tupla = ( 'a ' , 'b ' , 'c ' , 'd ' , 'e ')
>>> tupla [0]
'a '
>>> temp = a
>>> a = b
>>> b = temp
11.3 Tuplas como valores de retorno 147
Si tenemos que intercambiar variables muchas veces, el código tiende a ser engo-
rroso. Python proporciona una forma de asignación de tuplas que resuelve este
problema:
>>> a , b = b , a
>>> a , b , c , d = 1 , 2 , 3
ValueError : unpack tuple of wrong size
def intercambiar (x , y ):
return y , x
Así podemos asignar el valor de retorno a una tupla con dos variables:
a , b = intercambiar (a , b )
intercambio (a , b )
148 Tuplas
entonces a y x son dos alias para el mismo valor. Cambiar x dentro de intercambio
hace que x se refiera a un valor diferente, pero no tiene efecto en la a dentro de
__main__. Igualmente, cambiar y no tiene efecto en b.
Esta función se ejecuta sin errores, pero no hace lo que se pretende. Es un ejemplo
de error semántico.
La gran mayoría de los programas hacen lo mismo cada vez que se ejecutan, esto
es, son determinísticos. El determinismo generalmente es una buena propiedad,
ya que usualmente esperamos que los cálculos produzcan el mismo resultado. Sin
embargo, para algunas aplicaciones necesitamos que el computador sea imprede-
cible. Los juegos son un ejemplo inmediato, pero hay más.
Lograr que un programa sea verdaderamente no determinístico no es una tarea
fácil, pero hay formas de que parezca no determinístico. Una de ellas es generar
números aleatorios y usarlos para determinar la salida de un programa. Python
tiene una función primitiva que genera números pseudoaleatorios, que, aunque
no sean aleatorios desde el punto de vista matemático, sirven para nuestros pro-
pósitos.
El módulo random contiene una función llamada random que retorna un número
flotante entre 0.0 y 1.0. Cada vez que se llama a random, se obtiene el siguiente
número de una serie muy larga. Para ver un ejemplo ejecute el siguiente ciclo:
import random
Para generar un número aleatorio entre 0.0 y un límite superior como sup, multi-
plique x por sup.
11.5 Lista de números aleatorios 149
Vamos a generar función que cree una lista de números aleatorios listaAleatoria,
recibirá un parámetro entero que especifique el número de elementos a generar.
Primero, genera una lista de n ceros. Luego cada vez que itera en un ciclo for,
reemplaza uno de los ceros por un número aleatorio. El valor de retorno es una
referencia a la lista construida:
def listaAleatoria ( n ):
s = [0] * n
for i in range ( n ):
s [ i ] = random . random ()
return s
La probaremos con ocho elementos. Para depurar es una buena idea empezar con
pocos datos:
Los números generados por random deben distribuirse uniformemente, lo que sig-
nifica que cada valor es igualmente probable.
Si dividimos el rango de valores posibles en “regiones” del mismo tamaño y con-
tamos el número de veces que un valor aleatorio cae en cada región, deberíamos
obtener un resultado aproximado en todas las regiones.
Podemos probar esta hipótesis escribiendo un programa que divida el rango en
regiones y cuente el número de valores que caen en cada una.
150 Tuplas
11.6. Conteo
Un enfoque que funciona en problemas como éste es dividir el problema en sub-
problemas que se puedan resolver con un patrón computacional que ya sepamos.
En este caso, necesitamos recorrer una lista de números y contar el número de
veces que un valor cae en un rango dado. Esto parece familiar. En la Sección 8.8,
escribimos un programa que recorría una cadena y contaba el números de veces
que aparecía una letra determinada.
Entonces podemos copiar el programa viejo para adaptarlo posteriormente a nues-
tro problema actual. El original es:
cont = 0
for c in fruta :
if c == 'a ':
cont = cont + 1
print cont
El primer paso es reemplazar fruta con lista y c por num. Esto no cambia el
programa, sólo lo hace más legible.
El segundo paso es cambiar la prueba. No queremos buscar letras. Queremos ver
si num está entre dos valores dados inf y sup.
cont = 0
for num in lista :
if inf < num < sup :
cont = cont + 1
print cont
Usted puede confirmar que cada región tiene el mismo ancho, que no se solapan y
que cubren el rango completo de 0.0 a 1.0.
Ahora regresemos al primer problema. Necesitamos una manera de almacenar
ocho enteros, usando una variable para indicarlos uno a uno. Usted debe estar
pensando “¡una lista!”
Tenemos que crear la lista de regiones fuera del ciclo, porque esto sólo debe ocurrir
una vez. Dentro del ciclo, llamaremos a enRegion repetidamente y actualizaremos
el iésimo elemento de la lista:
numRegiones = 8
Regiones = [0] * numRegiones
ancho = 1.0 / numRegiones
for i in range ( numRegiones ):
inf = i * ancho
sup = inf + ancho
Regiones [ i ] = enRegion ( lista , inf , sup )
print Regiones
Con una lista de 1000 valores, este código produce la siguiente lista de conteo:
Todos estos valores están muy cerca a 125, que es lo que esperamos. Al menos,
están lo suficientemente cerca como para creer que el generador de números pseu-
doaleatorios está funcionando bien.
11.8 Una solución en una sola pasada 153
numRegiones = 8
Regiones = [0] * numRegiones
for i in lista :
ind = int ( i * numRegiones )
Regiones [ ind ] = Regiones [ ind ] + 1
11.9. Glosario
Tipo inmutable: es un tipo de dato en el que los elementos no pueden ser modifi-
cados. Las asignaciones a elementos o segmentos de tipos inmutables causan
errores. Las cadenas y las tuplas son inmutables.
154 Tuplas
Tipo mutable: tipo de dato en el que los elementos pueden ser modificados. Todos
los tipos mutables son compuestos. Las listas y los diccionarios son mutables.
Tupla: tipo de dato secuencial similar a la lista, pero inmutable. Las tuplas se pue-
den usar donde se requiera un tipo inmutable, por ejemplo como llaves de
un diccionario.
Asignación de tuplas: una asignación a todos los elementos de una tupla en una
sola sentencia. La asignación ocurre en paralelo y no secuencialmente. Es útil
para intercambiar valores de variables.
11.10. Ejercicios
Para cada función, agregue chequeo de tipos y pruebas unitarias.
1. Escriba una función mas_frecuentes que tome una cadena e imprima las le-
tras en orden descendente por frecuencia. Ejecútela con textos de diferentes
lenguajes y observe como varían las frecuencias de letras. Compare sus re-
sultados con las tablas en:
http://en.wikipedia.org/wiki/Letter_frequencies
Solución: http://thinkpython.com/code/most_frequent.py
Pista: cree un diccionario que asocie cada conjunto de letras a una lista de pa-
labras que puedan ser formadas con esas letras. ¿Como se puede representar
el conjunto de letras de forma que pueda ser usado como llave? Modifique el
programa que obtuvo para que imprima en orden descendente por tamaño
los conjuntos de anagramas. En el juego Scrabble, un ’bingo’ se da cuando se
juegan las 7 fichas, junto con otra letra en el tablero, para formar una palabra
de 8 letras. ¿Que conjunto de 8 letras forma los bingos mas posibles?
Solución: http://thinkpython.com/code/anagram_sets.py
4. ¿Cual es la palabra mas larga que sigue siendo válida a medida que se remue-
ven una a una sus letras? Por ejemplo, en inglés, ’sprite’ sin la ’r’ es ’spite’,
que sin la ’e’, es ’spit’, que sin la ’s’ es ’pit’, que sin la ’p’ es ’it’ que sin la ’t’
es ’i’. Las letras se pueden remover de cualquier posición, pero no se pueden
reordenar.
Escriba un programa que encuentre todas las palabras que pueden reducirse
de esta forma y que encuentre la mas larga.
Pistas:
Escriba una función que tome una palabra y calcule todas las palabras que
pueden formarse al removerle una letra. Estas son las palabras ’hijas’. Recur-
sivamente, una palabra es reducible si alguno de sus hijas es reducible. El
caso base lo da la cadena vacía.
Solución: http://thinkpython.com/code/reducible.py
156 Tuplas
Capítulo 12
Diccionarios
Los tipos compuestos que ha visto hasta ahora (cadenas, listas y tuplas) usan ente-
ros como índices. Si usted intenta usar cualquier otro tipo como índice provocará
un error.
Los diccionarios son similares a otros tipos compuestos, excepto en que pueden
usar como índice cualquier tipo inmutable. A modo de ejemplo, crearemos un dic-
cionario que traduzca palabras inglesas al español. En este diccionario, los índices
son cadenas (strings).
Una forma de crear un diccionario es empezar con el diccionario vacío y añadir
elementos. El diccionario vacío se expresa como {}:
>>> ing_a_esp = {}
>>> ing_a_esp [ ' one '] = ' uno '
>>> ing_a_esp [ ' two '] = ' dos '
La primera asignación crea un diccionario llamado ing_a_esp; las otras asignacio-
nes añaden nuevos elementos al diccionario. Podemos desplegar el valor actual
del diccionario del modo habitual:
>>> print ing_a_esp
{ ' one ': ' uno ' , ' two ': ' dos '}
Los elementos de un diccionario aparecen en una lista separada por comas. Cada
entrada contiene un índice y un valor separado por dos puntos (:). En un dicciona-
rio, los índices se llaman claves, por eso los elementos se llaman pares clave-valor.
157
158 Diccionarios
Otra forma de crear un diccionario es dando una lista de pares clave-valor con la
misma sintaxis que la salida del ejemplo anterior:
>>> ing_a_esp ={ ' one ': ' uno ' , ' two ': ' dos ' , ' three ': ' tres '}
La clave ’two’ nos da el valor ’dos’ aunque aparezca en el tercer par clave-valor.
>>> inventario = { ' manzanas ': 430 , ' bananas ': 312 ,
' naranjas ': 525 , ' peras ': 217}
>>> print inventario
{ ' naranjas ': 525 , ' manzanas ': 430 , ' peras ': 217 ,
' bananas ': 312}
Si alguien compra todas las peras, podemos eliminar la entrada del diccionario:
El método items devuelve ambos, una lista de tuplas con los pares clave-valor del
diccionario:
La sintaxis nos proporciona información muy útil acerca del tipo de datos. Los
corchetes indican que es una lista. Los paréntesis indican que los elementos de la
lista son tuplas.
Si un método acepta un argumento, usa la misma sintaxis que una llamada a una
función. Por ejemplo, el método has_key acepta una clave y devuelve verdadero
(1) si la clave aparece en el diccionario:
>>> opuestos = { ' arriba ': ' abajo ' , ' derecho ': ' torcido ' ,
' verdadero ': ' falso '}
>>> alias = opuestos
>>> copia = opuestos . copy ()
alias y opuestos se refieren al mismo objeto; copia se refiere a una copia nueva
del mismo diccionario. Si modificamos alias, opuestos también resulta cambiado:
Sólo hay tres pares clave-valor, uno para cada elemento de la matriz diferente de
cero. Cada clave es una tupla, y cada valor es un entero.
Para acceder a un elemento de la matriz, podemos usar el operador []:
get mejora sensiblemente la semántica del acceso a una matriz dispersa. ¡Lástima
que la sintaxis no sea tan clara!
12.5. Pistas
Si ha jugado con la función fibonacci de la Sección 6.7, es posible que haya no-
tado que cuanto más grande es el argumento que recibe, más tiempo le cuesta
ejecutarse. De hecho, el tiempo de ejecución aumenta muy rápidamente. En nues-
tra máquina, fibonacci(20) acaba instantáneamente, fibonacci(30) tarda más o
menos un segundo, y fibonacci(40) tarda una eternidad.
Para entender por qué, observe este gráfico de llamadas de fibonacci con n=4:
12.5 Pistas 163
fibonacci
n 4
fibonacci fibonacci
n 3 n 2
fibonacci fibonacci
n 1 n 0
def fibonacci ( n ):
if anteriores . has_key ( n ):
return anteriores [ n ]
else :
nuevoValor = fibonacci (n -1) + fibonacci (n -2)
164 Diccionarios
anteriores [ n ] = nuevoValor
return nuevoValor
>>> type (1 L )
< type ' long int ' >
La otra es usar la función long para convertir un valor en long int. long acepta
cualquier tipo numérico e incluso cadenas de dígitos:
Todas las operaciones matemáticas funcionan sobre los datos de tipo long int, así
que no tenemos que hacer mucho para adaptar fibonacci:
>>> cuentaLetras = {}
>>> for letra in " Mississippi " :
cuentaLetras [ letra ] = cuentaLetras . get ( letra , 0)+1
>>> cuentaLetras
{ 'M ': 1 , 's ': 4 , 'p ': 2 , 'i ': 4}
12.8. Glosario
Diccionario: es una colección de pares clave-valor que establece una correspon-
dencia entre claves y valores. Las claves pueden ser de cualquier tipo inmu-
table, los valores pueden ser de cualquier tipo.
Método: tipo de función al que se llama con una sintaxis diferente y al que se
invoca “sobre” un objeto.
12.9. Ejercicios
Para cada función, agregue chequeo de tipos y pruebas unitarias.
2. Escriba una función booleana que averigüe si una lista tiene algún elemento
duplicado usando un diccionario.
3. Una cadena de ARN puede representarse como una lista en la que los ele-
mentos pueden ser los caracteres A,C,G y U. Escriba una función booleana
que averigüe si una lista de caracteres es una cadena de ARN válida.
5. Escriba una función que reciba una cadena de ADN y cuente cuantos nucleó-
tidos de cada tipo tiene (cuantas veces tiene A,C,G y T) usando un dicciona-
rio.
7. Escriba una función producto que reciba una matriz dispersa M, implemen-
tada con un diccionario, y un número. Debe retornar la matriz que resulta de
multiplicar cada elemento de M por el número.
8. Escriba una función que reciba dos matrices dispersas, implementadas con
diccionarios, y las sume, produciendo otra matriz dispersa.
9. Escriba una función que reciba dos matrices dispersas, implementadas con
diccionarios, y las multiplique, produciendo otra matriz dispersa. Base su
solución en las dos soluciones anteriores.
10. Escriba una función booleana que reciba una matriz dispersa y averigüe si es
la matriz identidad.
168 Diccionarios
Capítulo 13
Archivos y excepciones
169
170 Archivos y excepciones
< open file ' test . dat ' , mode 'w ' at fe820 >
El cierre del archivo le dice al sistema que hemos terminado de escribir y deja el
archivo listo para leer:
>>> f . close ()
Ya podemos abrir el archivo de nuevo, esta vez para lectura, y poner su contenido
en una cadena. Esta vez el argumento de modo es "r", para lectura:
Como era de esperar, el método read lee datos del archivo. Sin argumentos, lee el
archivo completo:
No hay un espacio entre “hora” y “de” porque no escribimos un espacio entre las
cadenas. read también puede aceptar un argumento que le indica cuántos caracte-
res leer:
171
>>>
>>>
readlines devuelve todas las líneas que queden como una lista de cadenas:
>>> print f . readlines ()
[ ' línea dos \012 ' , ' línea tres \012 ']
En este caso, la salida está en forma de lista, lo que significa que las cadenas apare-
cen con comillas y el carácter de salto de línea aparece como la secuencia de escape
012.
Al final del archivo, readline devuelve una cadena vacía y readlines devuelve
una lista vacía:
>>> print f . readline ()
La sentencia continue termina la iteración actual del ciclo, pero sigue haciendo
las que le faltan. El flujo de ejecución pasa al principio del ciclo, comprueba la
condición y continúa normalmente.
Así, si texto es una cadena vacía, el ciclo termina. Si el primer carácter de texto
es una almohadilla (#), el flujo de ejecución va al principio del ciclo. Sólo si ambas
condiciones fallan copiamos texto en el archivo nuevo.
>>> x = 52
>>> f . write ( str ( x ))
>>> motos = 52
>>> " %d " % motos
' 52 '
El resultado es la cadena ’52’, que no debe confundirse con el valor entero 52.
Una secuencia de formato puede aparecer en cualquier lugar de la cadena de for-
mato, de modo que podemos incrustar un valor en una frase:
>>> motos = 52
>>> " En julio vendimos %d motos . " % motos
' En julio vendimos 52 motos . '
>>> " En %d dias ganamos %f millones de %s . " % (4 ,1.2 , ' pesos ')
' En 4 dias ganamos 1.200000 millones de pesos . '
' 62 '
>>> " %12 f " % 6.1
' 6.100000 '
En este ejemplo, el resultado ocupa doce espacios e incluye dos dígitos tras la co-
ma. Este formato es útil para imprimir cantidades de dinero con las comas alinea-
das.
Imagine, por ejemplo, un diccionario que contiene los nombres de los estudiantes
como clave y las tarifas horarias como valores. He aquí una función que imprime
el contenido del diccionario como de un informe formateado:
>>> tarifas = { ' maria ': 6.23 , ' jose ': 5.45 , ' jesus ': 4.25}
>>> informe ( tarifas )
jose 5.45
jesus 4.25
maria 6.23
176 Archivos y excepciones
Controlando el ancho de cada valor nos aseguramos de que las columnas van a
quedar alineadas, siempre que los nombre tengan menos de veintiún caracteres y
las tarifas sean menos de mil millones la hora.
13.3. Directorios
Cuando se crea un archivo nuevo abriéndolo y escribiendo, este va a quedar en el
directorio en uso (aquél en el que se estuviese al ejecutar el programa). Del mismo
modo, cuando se abre un archivo para leerlo, Python lo busca en el directorio en
uso.
Si usted quiere abrir un archivo de cualquier otro sitio, tiene que especificar la ruta
del archivo, que es el nombre del directorio (o carpeta) donde se encuentra este:
>>> f = open ( " / usr / share / dict / words " ," r " )
>>> print f . readline ()
Aarhus
13.4. Encurtido
Para poner valores en un archivo, se deben convertir a cadenas. Usted ya ha visto
cómo hacerlo con str:
>>> f . readline ()
' 12.3[1 , 2 , 3] '
Para almacenar una estructura de datos, se usa el método dump y luego se cierra el
archivo de la forma habitual:
Ahora podemos abrir el archivo para leer y cargar las estructuras de datos que
volcamos ahí:
Cada vez que invocamos load obtenemos un valor del archivo completo con su
tipo original.
178 Archivos y excepciones
13.5. Excepciones
Siempre que ocurre un error en tiempo de ejecución, se crea una excepción. Nor-
malmente el programa se para y Python presenta un mensaje de error.
Por ejemplo, la división por cero crea una excepción:
>>> a = []
>>> print a [5]
IndexError : list index out of range
>>> b = {}
>>> print b [ ' qué ']
KeyError : qué
En cada caso, el mensaje de error tiene dos partes: el tipo de error antes de los dos
puntos y detalles sobre el error después de los dos puntos. Normalmente, Python
también imprime una traza de dónde se encontraba el programa, pero la hemos
omitido en los ejemplos.
A veces queremos realizar una operación que podría provocar una excepción, pero
no queremos que se pare el programa. Podemos manejar la excepción usando las
sentencias try y except.
Por ejemplo, podemos preguntar al usuario por el nombre de un archivo y luego
intentar abrirlo. Si el archivo no existe, no queremos que el programa se aborte;
queremos manejar la excepción.
La sentencia try ejecuta las sentencias del primer bloque. Si no se produce ninguna
excepción, pasa por alto la sentencia except. Si ocurre cualquier excepción, ejecuta
las sentencias de la rama except y después continúa.
Podemos encapsular esta capacidad en una función: existe, que acepta un nombre
de archivo y devuelve verdadero si el archivo existe y falso si no:
Se pueden usar múltiples bloques except para manejar diferentes tipos de excep-
ciones. El Manual de Referencia de Python contiene los detalles.
Si su programa detecta una condición de error, se puede lanzar (raise en inglés)
una excepción. Aquí hay un ejemplo que acepta una entrada del usuario y com-
prueba si es 17. Suponiendo que 17 no es una entrada válida por cualquier razón,
lanzamos una excepción.
# -* - coding : utf -8 -* -
def tomaNumero () :
x = input ( ' Elige un número : ')
if x == 17 :
raise ' ErrorNumeroMalo ' , ' 17 es un mal número '
return x
>>> tomaNumero ()
Elige un número : 17
ErrorNumeroMalo : 17 es un mal número
180 Archivos y excepciones
13.6. Glosario
Archivo: entidad con nombre, normalmente almacenada en un disco duro, dis-
quete o CD-ROM, que contiene una secuencia de caracteres.
Sentencia break: es una sentencia que provoca que el flujo de ejecución salga de
un ciclo.
Manejar: impedir que una excepción detenga un programa utilizando las senten-
cias except y try.
13.7. Ejercicios
Para cada función, agregue chequeo de tipos y pruebas unitarias.
1. Escriba una función que use tomaNumero para leer un número del teclado y
que maneje la excepción ErrorNumeroMalo
4. Escriba una función que permita escribir una matriz, implementada como
una lista de listas, en un archivo.
5. Escriba una función que permita leer una matriz, implementada como una
lista de listas, de un archivo.
6. Escriba una función que permita escribir una matriz dispersa, implementada
con un diccionario, en un archivo.
7. Escriba una función que permita leer una matriz dispersa, implementada con
un diccionario, de un archivo.
182 Archivos y excepciones
Capítulo 14
Clases y objetos
class Punto :
pass
183
184 Clases y objetos
limpio = Punto ()
14.2. Atributos
Podemos añadir nuevos datos a una instancia utilizando la notación de punto:
La variable limpio apunta a un objeto Punto, que contiene dos atributos. Cada
atributo apunta a un número en punto flotante.
Podemos leer el valor de un atributo utilizando la misma sintaxis:
4.0
>>> x = limpio . x
>>> print x
3.0
La expresión limpio.x significa, “ve al objeto al que apunta limpio y toma el valor
de x”. En este caso, asignamos ese valor a una variable llamada x. No hay conflicto
entre la variable x y el atributo x. El propósito de la notación punto es identificar
de forma inequívoca a qué variable se refiere el programador.
Se puede usar la notación punto como parte de cualquier expresión. Así, las sen-
tencias que siguen son correctas:
print '( ' + str ( limpio . x ) + ' , ' + str ( limpio . y ) + ') '
d is t an c i aA l Cu a d ra d o = limpio . x * limpio . x +
limpio . y * limpio . y
La primera línea presenta (3.0, 4.0); la segunda línea calcula el valor 25.0.
Usted puede estar tentado a imprimir el propio valor de limpio:
El resultado indica que limpio es una instancia de la clase Punto que se definió en
__main__. 80f8e70 es el identificador único de este objeto, escrito en hexadecimal.
Probablemente ésta no es la manera más clara de mostrar un objeto Punto. En breve
veremos cómo cambiar esto.
def imprimePunto ( p ):
print '( ' + str ( p . x ) + ' , ' + str ( p . y ) + ') '
14.4. Mismidad
El significado de la palabra “mismo” parece totalmente claro hasta que uno se
detiene a pensarlo un poco y se da cuenta de que hay algo más de lo que se supone
comúnmente.
Por ejemplo, si alguien dice “Pepe y yo tenemos la misma moto”, lo que quiere
decir es que su moto y la de Pepe son de la misma marca y modelo, pero que son
dos motos distintas. Si dice “Pepe y yo tenemos la misma madre”, quiere decir que
su madre y la de Pepe son la misma persona1 . Así que la idea de “identidad” es
diferente según el contexto.
Cuando uno habla de objetos, hay una ambigüedad parecida. Por ejemplo, si dos
Puntos son el mismo, ¿significa que contienen los mismos datos (coordenadas) o
que son de verdad el mismo objeto?
Para averiguar si dos referencias se refieren al mismo objeto, se utiliza el operador
==. Por ejemplo:
>>> p1 = Punto ()
>>> p1 . x = 3
>>> p1 . y = 4
>>> p2 = Punto ()
>>> p2 . x = 3
>>> p2 . y = 4
>>> p1 == p2
False
>>> p2 = p1
>>> p1 == p2
True
Este tipo de igualdad se llama igualdad superficial, porque sólo compara las refe-
rencias, pero no el contenido de los objetos.
1 No todas las lenguas tienen el mismo problema. Por ejemplo, el alemán tiene palabras diferen-
tes para los diferentes tipos de identidad. “Misma moto” en este contexto sería “gleiche Motorrad” y
“misma madre” sería “selbe Mutter”.
14.5 Rectángulos 187
Para comparar los contenidos de los objetos (igualdad profunda) podemos escribir
una función llamada mismoPunto:
def mismoPunto ( p1 , p2 ) :
return ( p1 . x == p2 . x ) and ( p1 . y == p2 . y )
Si ahora creamos dos objetos diferentes que contienen los mismos datos podremos
usar mismoPunto para averiguar si representan el mismo punto:
>>> p1 = Punto ()
>>> p1 . x = 3
>>> p1 . y = 4
>>> p2 = Punto ()
>>> p2 . x = 3
>>> p2 . y = 4
>>> mismoPunto ( p1 , p2 )
True
Por supuesto, si las dos variables apuntan al mismo objeto mismoPunto devuelve
verdadero.
14.5. Rectángulos
Digamos que queremos una clase que represente un rectángulo. La pregunta es,
¿qué información tenemos que proporcionar para definir un rectángulo? Para sim-
plificar las cosas, supongamos que el rectángulo está orientado vertical u horizon-
talmente, nunca en diagonal.
Tenemos varias posibilidades: podemos señalar el centro del rectángulo (dos coor-
denadas) y su tamaño (ancho y altura); o podemos señalar una de las esquinas y
el tamaño; o podemos señalar dos esquinas opuestas. Un modo convencional es
señalar la esquina superior izquierda del rectángulo y el tamaño.
De nuevo, definiremos una nueva clase:
class Rectangulo :
pass
Y la instanciaremos:
188 Clases y objetos
caja = Rectangulo ()
caja . ancho = 100.0
caja . altura = 200.0
Este código crea un nuevo objeto Rectangulo con dos atributos flotantes. ¡Para
señalar la esquina superior izquierda podemos incrustar un objeto dentro de otro!
Para llamar a esta función, se pasa caja como argumento y se asigna el resultado
a una variable:
(50.0 , 100.0)
>>> b = Rectangulo ()
>>> b . ancho = 100.0
>>> b . altura = 200.0
>>> b . esquina = Punto ()
>>> b . esquina . x = 0.0;
>>> b . esquina . y = 0.0;
>>> agrandarRect (b , 50 , 100)
14.8. Copiado
El uso de un alias puede hacer que un programa sea difícil de leer, porque los
cambios hechos en un lugar pueden tener efectos inesperados en otro lugar. Es
difícil estar al tanto de todas las variables que pueden apuntar a un objeto dado.
Copiar un objeto es, muchas veces, una alternativa a la creación de un alias. El
módulo copy contiene una función llamada copy que puede duplicar cualquier
objeto:
Una vez que hemos importado el módulo copy, podemos usar el método copy para
hacer un nuevo Punto. p1 y p2 no son el mismo punto, pero contienen los mismos
datos.
Para copiar un objeto simple como un Punto, que no contiene objetos incrustados,
copy es suficiente. Esto se llama copiado superficial.
Para algo como un Rectangulo, que contiene una referencia a un Punto, copy no
lo hace del todo bien. Copia la referencia al objeto Punto, de modo que tanto el
Rectangulo viejo como el nuevo apuntan a un único Punto.
Si creamos una caja, b1, de la forma habitual y entonces hacemos una copia, b2,
usando copy, el diagrama de estados resultante se ve así:
b1 anchura 100.0 100.0 anchura b2
altura 200.0 x 0.0 200.0 altura
esquina y 0.0 esquina
14.9. Glosario
Clase: tipo compuesto definido por el usuario. También se puede pensar en una
clase como una plantilla para los objetos que son instancias de la misma.
Objeto: tipo de dato compuesto que suele usarse para representar una cosa o con-
cepto del mundo real.
Atributo: uno de los elementos de datos con nombre que constituyen una instan-
cia.
192 Clases y objetos
14.10. Ejercicios
1. Cree e imprima un objeto Punto y luego use id para imprimir el identificador
único del objeto. Traduzca el número hexadecimal a decimal y asegúrese de
que coincidan.
3. Escriba una función llamada mueveRect que tome un Rectangulo y dos pa-
rámetros llamados dx y dy. Tiene que cambiar la posición del rectángulo aña-
diendo en la esquina: dx a la coordenada x y dy a la coordenada y.
Clases y funciones
15.1. Hora
Como otro ejemplo de tipo de dato definido por el usuario definiremos una clase
llamada Hora:
class Hora :
pass
Ahora podemos crear un nuevo objeto Hora y asignarle atributos para las horas,
minutos y segundos:
tiempo = Hora ()
tiempo . hora = 11
tiempo . minutos = 59
tiempo . segundos = 30
193
194 Clases y funciones
def sumarHoras ( t1 , t2 ):
sum = Hora ()
sum . hora = t1 . hora + t2 . hora
sum . minutos = t1 . minutos + t2 . minutos
sum . segundos = t1 . segundos + t2 . segundos
return sum
La función crea un nuevo objeto Hora, inicializa sus atributos y retorna una refe-
rencia hacia el nuevo objeto. Esto se denomina función pura, porque no modifica
ninguno de los objetos que se le pasaron como parámetro ni tiene efectos laterales,
como desplegar un valor o leer entrada del usuario.
Aquí hay un ejemplo de uso de esta función. Crearemos dos objetos Hora: horaPan,
que contiene el tiempo que le toma a un panadero hacer pan y horaActual, que
contiene la hora actual. Luego usaremos sumarHoras para averiguar a qué hora
estará listo el pan. Si no ha terminado la función imprimirHora aún, adelántese a
la Sección 16.2 antes de intentar esto:
La salida de este programa es 12:49:30, que está correcta. Por otro lado, hay casos
en los que no funciona bien. ¿Puede pensar en uno?
El problema radica en que esta función no considera los casos donde el número
de segundos o minutos suman más de sesenta. Cuando eso ocurre tenemos que
“acarrear” los segundos extra a la columna de minutos. También puede pasar lo
mismo con los minutos.
Aquí hay una versión correcta:
def sumarHoras ( t1 , t2 ):
sum = Hora ()
sum . hora = t1 . hora + t2 . hora
sum . minutos = t1 . minutos + t2 . minutos
sum . segundos = t1 . segundos + t2 . segundos
return sum
15.3. Modificadoras
A veces es deseable que una función modifique uno o varios de los objetos que
recibe como parámetros. Usualmente, el código que hace el llamado a la función
conserva una referencia a los objetos que está pasando, así que cualquier cambio
que la función les haga será evidenciado por dicho código. Este tipo de funciones
se denominan modificadoras.
196 Clases y funciones
return h
La primera línea ejecuta la operación básica, las siguientes consideran los casos
especiales que ya habíamos visto.
¿Es correcta esta función? ¿Que pasa si el parámetro segundos es mucho más gran-
de que sesenta? En ese caso, no sólo es suficiente añadir uno, tenemos que sumar
de uno en uno hasta que segundos sea menor que sesenta. Una solución consiste
en reemplazar las sentencias if por sentencias while:
return hora
15.4 ¿Cual es el mejor estilo? 197
Usted debe pensar unos minutos para convencerse de que esta técnica sí convierte,
de una base a otra, correctamente. Asumiendo que ya está convencido, se pueden
usar las funciones anteriores para reescribir sumarHoras:
def sumarHoras ( t1 , t2 ):
segundos = c on ve rti rA Se gun do s ( t1 ) + c onv er ti rAS eg un dos ( t2 )
return crearHora ( segundos )
Esta versión es mucho más corta que la original, y es mucho más fácil de demostrar
que es correcta (asumiendo, como de costumbre, que las funciones que llama son
correctas).
15.6 Generalización 199
15.6. Generalización
Desde cierto punto de vista, convertir de base 60 a base 10 y viceversa es más
difícil que calcular solamente con horas. La conversión de bases es más abstracta,
mientras que nuestra intuición para manejar horas está más desarrollada.
Pero si tenemos la intuición de tratar las horas como números en base 60 y hace-
mos la inversión de escribir las funciones de conversión (convertirASegundos y
crearHora), obtenemos un programa más corto, legible, depurable y confiable.
También facilita la adición de más características. Por ejemplo, piense en el pro-
blema de restar dos Horas para averiguar el tiempo que transcurre entre ellas. La
solución ingenua haría resta llevando préstamos. En cambio, usar las funciones de
conversión sería mas fácil.
Irónicamente, algunas veces el hacer de un problema algo más difícil (o más gene-
ral) lo hace más fácil (porque hay menos casos especiales y menos oportunidades
para caer en errores).
15.7. Algoritmos
Cuando usted escribe una solución general para una clase de problemas, en vez
de encontrar una solución específica a un solo problema, ha escrito un algoritmo.
Mencionamos esta palabra antes, pero no la definimos cuidadosamente. No es fácil
de definir, así que intentaremos dos enfoques.
Primero, considere algo que no es un algoritmo. Cuando usted aprendió a multipli-
car dígitos, probablemente memorizó la tabla de multiplicación. De hecho, usted
memorizó 100 soluciones específicas. Este tipo de conocimiento no es algorítmico.
Pero si usted fuera “perezoso,” probablemente aprendió a hacer trampa por medio
de algunos trucos. Por ejemplo, para encontrar el producto entre n y 9, usted puede
escribir n − 1 como el primer dígito y 10 − n como el segundo. Este truco es una
solución general para multiplicar cualquier dígito por el 9. ¡Este es un algoritmo!
Similarmente, las técnicas que aprendió para hacer suma con acarreo ( llevando
para la columna hacia la derecha), resta con préstamos, y división larga, todas
son algoritmos. Una de las características de los algoritmos es que no requieren
inteligencia para ejecutarse. Son procesos mecánicos en el que cada paso sigue al
anterior de acuerdo con un conjunto de reglas sencillas.
200 Clases y funciones
En nuestra opinión, es vergonzoso que los seres humanos pasemos tanto tiempo
en la escuela aprendiendo a ejecutar algoritmos que, literalmente, no requieren
inteligencia.
Por otro lado, el proceso de diseñar algoritmos es interesante, intelectualmente
desafiante y una parte central de lo que denominamos programación.
Algunas cosas que la gente hace naturalmente sin dificultad o pensamiento cons-
ciente, son las mas difíciles de expresar algorítmicamente. Entender el lenguaje
natural es una de ellas. Todos lo hacemos, pero hasta ahora nadie ha sido capaz de
explicar como lo hacemos, al menos no con un algoritmo.
15.8. Glosario
Función pura: función que no modifica ninguno de los objetos que recibe como
parámetros. La mayoría de las funciones puras son fructíferas.
Modificadora: función que cambia uno o varios de los objetos que recibe como
parámetros. La mayoría de los modificadoras no retornan nada.
15.9. Ejercicios
1. Reescriba la función incrementar de forma que no contenga ciclos y siga
siendo correcta.
15.9 Ejercicios 201
4. Escriba una función imprimirHora que reciba un objeto Hora como argumen-
to y lo imprima de la forma horas:minutos:segundos.
5. Escriba una función booleana despues que reciba dos objetos Hora, t1 y t2
como argumentos, y retorne cierto si t1 va después de t2 cronológicamente
y falso en caso contrario.
202 Clases y funciones
Capítulo 16
Clases y métodos
203
204 Clases y métodos
La sintaxis para llamar o invocar un método es distinta que para las funcio-
nes.
16.2. imprimirHora
En el capítulo 15, definimos una clase denominada Hora y usted escribió una fun-
ción denominada imprimirHora, que lucía así:
class Hora :
pass
def imprimirHora ( h ):
print str ( h . hora ) + " : " +
str ( h . minutos ) + " : " +
str ( h . segundos )
Para convertir imprimirHora en un método todo lo que tenemos que hacer es po-
nerla adentro de la definición de clase. Note como ha cambiado la indentación.
class Hora :
def imprimirHora ( h ):
print str ( h . hora ) + " : " +
str ( h . minutos ) + " : " +
str ( h . segundos )
Como de costumbre, el objeto en el que el método se llama aparece antes del punto
y el nombre del método va a la derecha. El objeto al cual se invoca el método se
asigna al primer parámetro, así que horaActual se asigna al parámetro h.
Por convención, el primer parámetro de un método se denomina self (en inglés,
eso es algo como “sí mismo”). La razón para hacerlo es un poco tortuosa, pero se
basa en una metáfora muy útil.
La sintaxis para una llamada de función, imprimirHora(horaActual), sugiere que
la función es el agente activo. Dice algo como “Hey, imprimirHora! Aquí hay un
objeto para que imprimas”.
En la programación orientada a objetos, los objetos son los agentes activos. Una in-
vocación como horaActual.imprimirHora() dice algo como “Hey, objeto horaActual!
Por favor, imprímase a sí mismo!”.
Este cambio de perspectiva parece ser sólo “cortesía”, pero puede ser útil. En los
ejemplos que hemos visto no lo es. Pero, el transferir la responsabilidad desde las
funciones hacia los objetos hace posible escribir funciones más versátiles y facilita
la reutilización y el mantenimiento de código.
206 Clases y métodos
class Hora :
# Las definiciones anteriores van aquí ...
return self
class Hora :
# Las definiciones anteriores van aqui ...
Casi se puede leer el llamado en lenguaje natural:“Si la hora para Comer viene
después de la hora Actual, entonces ...”.
El tercer parámetro, ini, es opcional, ya que tiene un valor por defecto, 0. Si lla-
mamos a buscar con dos argumentos, se usa el valor por defecto y la búsqueda se
hace desde el principio de la cadena:
class Punto :
def __init__ ( self , x =0 , y =0):
self . x = x
self . y = y
>>> p = Punto (3 , 4)
>>> str ( p )
'(3 , 4) '
Imprimir un objeto Punto implícitamente invoca a __str__ o sobre éste, así que
definir a __str__ también cambia el comportamiento de la sentencia print:
>>> p = Punto (3 , 4)
>>> print p
(3 , 4)
Cuando escribimos una nueva clase, casi siempre empezamos escribiendo __init__,
ya que facilita la instanciación de objetos, y __str__, que casi siempre es esencial
para la depuración.
class Punto :
# los métodos definidos previamente van aquí ...
>>> p1 = Punto (3 , 4)
>>> p2 = Punto (5 , 7)
>>> p3 = p1 + p2
>>> print p3
(8 , 11)
>>> p1 = Punto (3 , 4)
>>> p2 = Punto (5 , 7)
>>> print p1 * p2
43
>>> print 2 * p2
(10 , 14)
>>> print p2 * 2
AttributeError : ' int ' object has no attribute 'x '
16.9. Polimorfismo
La mayoría de los métodos que hemos escrito sólo funcionan para un tipo de da-
to específico. Cuando se crea un nuevo tipo de objeto, se escriben métodos que
operan sobre ese tipo.
Pero hay ciertas operaciones que se podrían aplicar a muchos tipos, un ejemplo de
éstas son las operaciones aritméticas de las secciones anteriores. Si muchos tipos
soportan el mismo conjunto de operaciones, usted puede escribir funciones que
trabajen con cualquiera de estos tipos.
16.9 Polimorfismo 213
Por ejemplo la operación multsuma (que se usa en el álgebra lineal) toma tres pa-
rámetros, multiplica los primeros dos y luego suma a esto el tercero. En Python se
puede escribir así:
def multsuma (x , y , z ):
return x * y + z
>>> multsuma (3 , 2 , 1)
7
O sobre Puntos:
>>> p1 = Punto (3 , 4)
>>> p2 = Punto (5 , 7)
>>> print multsuma (2 , p1 , p2 )
(11 , 15)
>>> print multsuma ( p1 , p2 , 1)
44
def derechoyAlReves ( l ):
import copy
r = copy . copy ( l )
r . reverse ()
print str ( l ) + str ( r )
214 Clases y métodos
Como el método reverse es una función modificadora, tenemos que tomar la pre-
caución de hacer una copia de la lista antes de llamarlo. De esta forma la lista que
llega como parámetro no se modifica.
Aquí hay un ejemplo que aplica derechoyAlReves a una lista:
>>> miLista = [1 , 2 , 3 , 4]
>>> derechoyAlReves ( miLista )
[1 , 2 , 3 , 4][4 , 3 , 2 , 1]
Por supuesto que funciona para listas, esto no es sorprendente. Lo que sería sor-
prendente es que pudiéramos aplicarla a un Punto.
Para determinar si una función puede aplicarse a un nuevo tipo de dato usamos la
regla fundamental del polimorfismo:
16.10. Glosario
Lenguaje orientado a objetos: lenguaje que tiene características, como las clases
definidas por el usuario y la herencia, que facilitan la programación orienta-
16.11 Ejercicios 215
da a objetos.
Método: función que se define adentro de una clase y se llama sobre instancias de
ésta.
Producto punto: operación del álgebra lineal que multiplica dos Puntos y produce
un valor numérico.
Multiplicación escalar: operación del álgebra lineal que multiplica cada una de
las coordenadas de un Punto por un valor numérico.
Polimórfica: función que puede operar sobre varios tipos de datos. Si todas las
operaciones que se llaman dentro de la función se le pueden aplicar al tipo
de dato, entonces la función puede aplicársela al tipo.
16.11. Ejercicios
1. Convierta convertirASegundos (de la Sección 15.5) a un método de la clase
Hora.
217
218 Interludio 2: Creando un nuevo tipo de datos
class Fraccion :
...
def __str__ ( self ):
return " %d/ %d " % ( self . numerador , self . denominador )
class Fraccion :
...
def __mul__ ( self , otro ):
return Fraccion ( self . numerador * otro . numerador ,
self . denominador * otro . denominador )
Como __rmul__ tiene el mismo código que __mul__, y el método __mul__ puede
recibir un parámetro entero, nuestra multiplicación de fracciones funciona bien.
class Fraccion :
...
def __add__ ( self , otro ):
if type ( otro ) == type (5):
otro = Fraccion ( otro )
return Fraccion ( self . numerador * otro . denominador +
self . denominador * otro . numerador ,
self . denominador * otro . denominador )
__radd__ = __add__
Podemos probar estos métodos con objetos Fraccion y con números enteros.
Los primeros ejemplos llaman al método __add__; el último ejemplo llama al mé-
todo __radd__.
17.3 El algoritmo de Euclides 221
def MDC (m , n ):
if m % n == 0:
return n
else :
return MDC (n , m %n )
class Fraccion :
def __init__ ( self , numerador , denominador =1):
g = MDC ( numerador , denominador )
self . numerador = numerador / g
self . denominador = denominador / g
Ahora, cada vez que creamos una nueva Fraccion, ¡se simplifica!.
222 Interludio 2: Creando un nuevo tipo de datos
Una característica adicional que nos provee MDC es que si la fracción es negativa, el
signo menos siempre se mueve hacia el numerador.
class Fraccion :
...
def __cmp__ ( self , otro ):
dif = ( self . numerador * otro . denominador -
otro . numerador * self . denominador )
return dif
Si self es mayor que otro, entonces dif será positivo. Si otro es mayor, dif será
negativo. Si son iguales, dif es cero.
17.5 Extendiendo las fracciones 223
17.6. Glosario
Máximo divisor común (MDC): el entero positivo más grande que divide exacta-
mente a dos números (por ejemplo, el numerador y el denominador en una
fracción).
17.7. Ejercicios
1. Complemente la implementación de la clase Fraccion para que soporte de-
nominadores y numeradores de tipo long (enteros grandes).
Conjuntos de objetos
18.1. Composición
En este momento usted ya ha visto varios ejemplos de composición. Uno de los
primeros fue una invocación de un método como parte de una expresión. Otro
ejemplo es la estructura anidada de sentencias; por ejemplo, se puede colocar una
sentencia if dentro de un ciclo while, dentro de otra sentencia if.
Después de observar esto y haber aprendido sobre listas y objetos no debería sor-
prenderse al saber que se pueden crear listas de objetos. También pueden crearse
objetos que contengan listas (como atributos), listas que contengan listas, objetos
que contengan objetos, y así sucesivamente.
En este capítulo y el siguiente, mostraremos algunos ejemplos de estas combina-
ciones, usando objetos Carta.
225
226 Conjuntos de objetos
del As puede ser más alto que el de un rey o más bajo que 2.
Si deseamos definir un nuevo objeto para representar una carta del naipe, parece
obvio que los atributos deberían ser valor y figura. No es tan obvio que tipo de
dato asignar a estos atributos. Una posibilidad consiste en usar cadenas de texto
con palabras como Picas para las figuras y Reina para los valores. Un problema de
esta implementación es que no sería tan fácil comparar cartas para ver cuál tiene
un valor mayor o una figura mayor.
Una alternativa consiste en usar enteros para codificar los valores y las figuras.
Por “codificar”, no estamos haciendo alusión a encriptar o traducir a un código
secreto. Lo que un científico de la computación considera “codificar” es “definir
una correspondencia entre una secuencia de números y los objetos que deseamos
representar”. Por ejemplo:
Picas 7→ 3
Corazones 7→ 2
Diamantes 7→ 1
Tréboles 7→ 0
Una característica notable de esta correspondencia es que las figuras aparecen en
orden decreciente de valor así como los enteros van disminuyendo. De esta forma,
podemos comparar figuras mediante los enteros que las representan. Una corres-
pondencia para los valores es bastante sencilla; cada número se relaciona con el
entero correspondiente, y para las cartas que se representan con letras tenemos lo
siguiente:
A 7→ 1
J 7→ 11
Q 7→ 12
K 7→ 13
La razón para usar notación matemática en estas correspondencias es que ellas no
hacen parte del programa en Python. Son parte del diseño, pero nunca aparecen
explícitamente en el código fuente. La definición de la clase Carta luce así:
class Carta :
def __init__ ( self , figura =0 , valor =0):
self . figura = figura
self . valor = valor
18.3 Atributos de clase y el método __str__ 227
tresTreboles = Carta (0 , 3)
class Carta :
listaFiguras = [ " Treboles " , " Diamantes " , " Corazones " ,
" Picas " ]
listaValores = [ " narf " , " As " , " 2 " , " 3 " , " 4 " , " 5 " , " 6 " ,
" 7 " ," 8 " , " 9 " , " 10 " , " Jota " , " Reina " , " Rey " ]
Un atributo de clase se define afuera de los métodos y puede ser accedido desde
cualquiera de ellos.
Dentro de __str__, podemos usar a listaFiguras y listaValores para esta-
blecer una correspondencia entre los valores numéricos de figura, valor y los
nombres de las cartas. La expresión self.listaFiguras[self.figura] significa
“use el atributo figura del objeto self como índice dentro del atributo de clase
listaFiguras, esto seleccionará la cadena de texto apropiada”.
La razón para el “narf” en el primer elemento de listaValores consiste en ocupar
el elemento cero de la lista que no va a ser usado en el programa. Los valores
228 Conjuntos de objetos
Los atributos de clase como listaFiguras se comparten por todos los objetos
Carta. La ventaja de esto es que podemos usar cualquier objeto Carta para ac-
ceder a ellos:
>>> c2 = Carta (1 , 3)
>>> print c2
3 de Diamantes
>>> print c2 . listaFiguras [1]
Diamantes
>>> print c2
3 de Rombos rojos
al otro. Para los tipos definidos por el programador podemos sobrecargar el com-
portamiento de los operadores predefinidos proporcionando un método llamado
__cmp__. Por convención, __cmp__ toma dos parámetros, self y otro, y retorna 1
si el primer objeto es más grande, -1 si el segundo es más grande y 0 si son iguales
entre si.
Algunos tipos tienen un orden total, lo que quiere decir que cualquier pareja de
elementos se puede comparar para decidir cuál de ellos es mayor. Por ejemplo, los
números enteros y los de punto flotante tienen un orden total. Algunos conjuntos
no tienen relación de orden, lo que quiere decir que no hay una manera sensata
de determinar que un elemento es mayor que otro. Por ejemplo, las frutas no tie-
nen una relación de orden, y esta es la razón por la que no se pueden comparar
manzanas con naranjas.
El conjunto de cartas tiene un orden parcial, lo que quiere decir que algunas veces
se pueden comparar elementos, y otras veces no. Por ejemplo, el 3 de Picas es
mayor que el 2 de picas, y el 3 de Diamantes es mayor que el 3 de Picas. Pero, ¿que
es más alto, el 3 de Picas o el 2 de Diamantes? Uno tiene un valor más alto, pero el
otro tiene una figura más alta.
Para lograr comparar las cartas, hay que tomar una decisión sobre la importancia
del valor y de la figura. Para ser honestos, esta decisión es arbitraria. Así que toma-
remos la opción de determinar qué figura es más importante, basándonos en que
un mazo de cartas nuevo viene con las Picas (en orden), luego los Diamantes, y así
sucesivamente.
Con esta decisión __cmp__ queda así:
Con este orden los Ases valen menos que los Dos.
230 Conjuntos de objetos
18.5. Mazos
Ahora que tenemos objetos para representar Cartas, el siguiente paso lógico con-
siste en definir una clase para representar un Mazo. Por supuesto, un mazo (o ba-
raja) está compuesto por cartas, así que cada instancia de Mazo contendrá como
atributo una lista de cartas.
La siguiente es la definición de la clase Mazo. El método de inicialización crea el
atributo cartas y genera el conjunto usual de cincuenta y dos cartas:
class Mazo :
def __init__ ( self ):
self . cartas = []
for figura in range (4):
for valor in range (1 , 14):
self . cartas . append ( Carta ( figura , valor ))
La forma más sencilla de llenar el mazo consiste en usar un ciclo anidado. El ciclo
exterior enumera las figuras de 0 a 3. El ciclo interno enumera los valores de 1 a 13.
Como el ciclo exterior itera cuatro veces y el interno itera trece veces, el número
total de iteraciones es cincuenta y dos (4 × 13). Cada iteración crea una nueva
instancia de Carta y la pega a la lista cartas.
El método append acepta secuencias mutables como las listas y no acepta tuplas.
class Mazo :
...
def imprimirMazo ( self ):
for carta in self . cartas :
print carta
18.6 Imprimiendo el mazo 231
En este ejemplo y en los que siguen, los puntos suspensivos indican que hemos
omitido los otros métodos de la clase.
Otra alternativa a imprimirMazo puede ser escribir un método __str__ para la
clase Mazo. La ventaja de __str__ radica en su mayor flexibilidad. Además de im-
primir el contenido del objeto, genera una representación de él en una cadena de
texto que puede manipularse en otros lugares del programa, incluso antes de im-
primirse.
A continuación hay una versión de __str__ que retorna una representación de un
Mazo. Para añadir un estilo de cascada, cada carta se imprime un espacio mas hacia
la derecha que la anterior:
class Mazo :
...
def __str__ ( self ):
s = ""
for i in range ( len ( self . cartas )):
s = s + " " * i + str ( self . cartas [ i ]) + " \ n "
return s
Este ejemplo demuestra varios puntos. Primero, en vez de recorrer los elementos
de la lista self.cartas, estamos usando a i como variable de ciclo que lleva la
posición de cada elemento en la lista de cartas.
Segundo, estamos usando el operador multiplicación aplicado a un número y una
cadena, de forma que la expresión " "*i produce un número de espacios igual al
valor actual de i.
Tercero, en vez de usar el comando print para realizar la impresión, utilizamos
la función str. Pasar un objeto como argumento a str es equivalente a invocar el
método __str__ sobre el objeto.
Finalmente, estamos usando a la variable s como acumulador. Inicialmente s es la
cadena vacía. En cada iteración del ciclo se genera una nueva cadena y se conca-
tena con el valor viejo de s para obtener el nuevo valor. Cuando el ciclo finaliza, s
contiene la representación completa del Mazo, que se despliega (parcialmente) así:
2 de Picas
3 de Picas
4 de Picas
5 de Picas
6 de Picas
7 de Picas
8 de Picas
9 de Picas
10 de Picas
J de Picas
Reina de Picas
Rey de Picas
As de Diamantes
Una manera sencilla de barajar el mazo consiste en recorrer todas las cartas in-
tercambiando cada una con otra carta escogida al azar. Es posible que la carta
se intercambie consigo misma, pero esto no causa ningún problema. De hecho,
18.8 Eliminando y entregando cartas 233
class Mazo :
...
def barajar ( self ):
import random
nCartas = len ( self . cartas )
for i in range ( nCartas ):
j = random . randrange (i , nCartas )
self . cartas [ i ] , self . cartas [ j ] = self . cartas [ j ] ,
self . cartas [ i ]
class Mazo :
...
def eliminarCarta ( self , carta ):
if carta in self . cartas :
self . cartas . remove ( carta )
return True
else :
return True
234 Conjuntos de objetos
class Mazo :
...
def entregarCarta ( self ):
return self . cards . pop ()
En realidad, pop elimina la última carta de la lista, así que realmente estamos en-
tregando cartas por la parte inferior, y esto no causa ningún inconveniente.
Una operación más que podemos requerir es la función booleana estaVacio, que
retorna True si el mazo está vacío:
class Mazo :
...
def estaVacio ( self ):
return ( len ( self . cartas ) == 0)
18.9. Glosario
Codificar: representar un conjunto de valores usando otro conjunto de valores es-
tableciendo una correspondencia entre ellos.
Atributo de clase: variable de una clase que está fuera de todos los métodos. Pue-
de ser accedida desde todos los métodos y se comparte por todas las instan-
cias de la clase.
Acumulador: variable que se usa para acumular una serie de valores en un ciclo.
Por ejemplo, concatenar varios valores en una cadena o sumarlos.
18.10 Ejercicios 235
18.10. Ejercicios
1. Modifique __cmp__ para que los Ases tengan mayor puntaje que los reyes.
Herencia
19.1. Definición
237
238 Herencia
Esta sentencia indica que la nueva clase Mano hereda de la clase Mazo.
El constructor de Mano inicializa los atributos nombre y cartas. La cadena nombre
identifica cada mano, probablemente con el nombre del jugador que la sostiene.
Por defecto, el parámetro nombre es una cadena vacía. cartas es la lista de cartas
en la mano, inicializada a una lista vacía:
self . cartas = []
self . nombre = nombre
Para la gran mayoría de juegos de cartas es necesario agregar y remover cartas del
mazo. Como Mano hereda eliminarCarta de Mazo, ya tenemos la mitad del trabajo
hecho, solo tenemos que escribir agregarCarta:
Recuerde que la elipsis (...) indica que estamos omitiendo los otros métodos. El
método append de las listas permite agregar la nueva carta.
class Mazo :
...
def repartir ( self , manos , nCartas =999):
nManos = len ( manos )
for i in range ( nCartas ):
if self . estaVacia ():
break # se detiene si no hay cartas
# toma la carta en el tope
240 Herencia
Aunque es conveniente heredar los métodos existentes, hay un dato adicional que
un objeto Mano puede incluir cuando se imprime, para lograr esto implementamos
__str__ sobrecargando el que está definido en la clase Mazo:
Inicialmente, s es una cadena que identifica la mano. Si está vacía, se añade la ca-
dena esta vacia. Si esto no es así se añade la cadena contiene y la representación
textual de la clase Mazo, que se obtiene aplicando el método __str__ a self.
Parece extraño aplicar el método __str__ de la clase Mazo a self que se refiere a la
Mano actual. Para disipar cualquier duda, recuerde que Mano es una clase de Mazo.
Los objetos Mano pueden hacer todo lo que los objetos Mazo hacen, así que esto es
legal.
En general, siempre se puede usar una instancia de una subclase en vez de una
instancia de la clase padre.
class JuegoDeCartas :
def __init__ ( self ):
self . mazo = Mazo ()
self . mazo . barajar ()
En este ejemplo vemos que la inicialización realiza algo más importante que asig-
nar valores iniciales a los atributos.
242 Herencia
Empezamos haciendo una copia de la lista de cartas, de forma que podamos re-
correrla y simultáneamente eliminar cartas. Como self.cartas se modifica en el
ciclo, no vamos a utilizarlo para controlar el recorrido. ¡Python puede confundirse
totalmente si empieza a recorrer una lista que está cambiando!
Para cada carta en la mano, averiguamos si se empareja con una carta escogida
de la mano de otra persona. Para esto, tienen que tener el mismo valor y la otra
figura del mismo color. La expresión 3 - carta.figura convierte un trébol (figura
0) en una Pica (figura 3) y a un Diamante (figura 1) en un Corazón (figura 2).
Usted puede comprobar que las operaciones inversas también funcionan. Si hay
una carta que se empareje, las dos se eliminan.
El siguiente ejemplo demuestra cómo usar quitarPareja:
5 de Treboles
Jota de Diamantes
10 de Diamantes
10 de Corazones
cont es un acumulador que lleva cuenta del número de parejas que se encontraron
en cada mano.
Cuando el número total de parejas encontradas llega a ser veinticinco, se han qui-
tado cincuenta cartas de las manos, y esto implica que la única carta que resta es la
reina de Picas y que el juego ha terminado.
La variable turno lleva la pista de cual es el jugador que tiene el turno para jugar.
Empieza en 0 y se incrementa de uno en uno; cuando alcanza el valor numManos, el
operador residuo lo reinicia en 0.
El método jugarUnTurno toma un parámetro que indica el jugador que tiene el
turno. El valor de retorno es el número de parejas encontradas durante este turno:
Si la mano de un jugador está vacía, este jugador está fuera del juego, así que no
hace ninguna acción y retorna 0.
Si esto no es así, un turno consiste en encontrar el primer jugador en la izquierda
que tenga cartas, tomar una de él, y buscar por parejas. Antes de retornar, las cartas
en la mano se barajan para que la elección del siguiente jugador sea al azar.
El método encontrarVecino comienza con el jugador a la izquierda y continua
buscando de manera circular hasta que encuentra un jugador que tenga cartas:
Si encontrarVecino diera toda la vuelta sin encontrar cartas, retornaría None y cau-
saría un error en algún otro lugar del programa. Afortunadamente, usted puede
comprobar que esto nunca va a pasar (siempre y cuando el fin del juego se detecte
correctamente).
Hemos omitido el método imprimaManos. Usted puede escribirlo.
La siguiente salida es de un juego en el que solo las primeras 15 cartas mas altas
(con valor 10 y superior) se repartieron a tres jugadores. Con este pequeño mazo,
el juego termina después de siete parejas encontradas, en vez de veinticinco.
Rey de Treboles
10 de Picas
10 de Corazones
10 de Treboles
19.8. Glosario
Herencia: es la capacidad de definir una clase, modificando una clase definida
previamente.
Clase hija: nueva clase creada por medio de herencia, también recibe el nombre
de “subclase.”
250 Herencia
19.9. Ejercicios
1. Escriba imprimaManos que recorre la lista self.manos e imprime cada mano.
2. Con base en la clase secuenciaADN, escriba una clase más general que se
denomine biosecuencia que maneje el alfabeto de la biosecuencia como un
atributo.
3. Agregue un método para chequear que la secuencia está bien formada, esto
es, cada elemento pertenece al alfabeto.
Capítulo 20
20.1. Motivación
El programa utiliza la biblioteca Kivy, que permite realizar interfaces gráficas que
corran en celulares, tablets (con pantallas sensibles al tacto) y computadores tradi-
cionales (corriendo MacOS X, Windows y Linux). Hay que conseguir el instalador
de:
http://kivy.org
http://cic.javerianacali.edu.co/~abecerra/files/triqui-kivy.zip
Para comprender el capítulo hay que ejecutar cada versión del programa a me-
dida que se avanza en la lectura. En las secciones a continuación se discuten los
fragmentos del programa a medida que se van agregando, cada fragmento tiene el
nombre del archivo en que se introdujo como comentario inicial.
251
252 Triqui con interfaz gráfica
# triqui0 . py
import kivy
kivy . require ( ' 1.8.0 ')
from kivy . app import App
from kivy . uix . gridlayout import GridLayout
El gran ciclo reside en la clase App de Kivy, de la que heredamos la clase Programa.
Cuando arranca el programa, al ejecutar run() se corre este gran ciclo que procesa
eventos.
El método build de programa retorna un objeto Triqui, que es nuestra ventana. La
clase Triqui hereda de GridLayout, una ventana que contiene elementos en dispo-
sición de cuadrícula (como una matriz). El método init de Triqui llama al método
init de la clase madre y le pasa una lista con un número de argumentos variable
(**kwargs).
Por ahora nuestra ventana, instancia de la clase Triqui, está vacía.
20.4. Widgets
Las ventanas suelen tener muchos elementos gráficos como menús, botones, pane-
les entre otros. En bibliotecas como kivy se llaman widgets. Por ejemplo, un botón
es un tipo de widget que se define en la clase Button.
Como el flujo de los programas gráficos no está determinado por el programador,
sino por el usuario al interactuar con los widgets de la ventana, el mecanismo que
se utiliza para reaccionar ante los eventos es el de registrar métodos que serán
invocados automáticamente por el gran ciclo.
# triqui1 . py
class Triqui ( GridLayout ):
def __init__ ( self , ** kwargs ):
super ( Triqui , self ). __init__ (** kwargs )
254 Triqui con interfaz gráfica
Los widgets se agregan a una ventana mediante el método add_widget. Aquí agre-
gamos un botón y registramos un método que responde al evento de presionarlo
(on_press). Por ahora el método no hace nada.
20.5. El Triqui
A continuación, definimos la geometría de la ventana como una matriz de 3 filas y
3 columnas en la que cada elemento es un botón. Ahora, en el método boton_presionado
vamos a mostrar un cuadro de diálogo que muestra un texto sencillo.
# triqui2 . py
class Triqui ( GridLayout ):
def __init__ ( self , ** kwargs ):
super ( Triqui , self ). __init__ (** kwargs )
self . cols = 3
self . rows = 3
for i in range (3):
for j in range (3):
self . add_widget ( Button ( font_size =100 ,
on_press = self . boton_presionado ))
MostrarMensaje es una clase que heredamos de PopUp, la clase que tiene kivy para
cuadros de diálogo:
# triqui2 . py
class MostrarMensaje ( Popup ):
def __init__ ( self , titulo , mensaje , ** kwargs ):
20.6 Jugando por turnos 255
El cuadro de diálogo tiene un título y un botón que, al ser presionado, cierra todo
el cuadro.
# triqui3 . py
class Triqui ( GridLayout ):
def __init__ ( self , ** kwargs ):
super ( Triqui , self ). __init__ (** kwargs )
self . cols = 3
self . rows = 3
for i in range (3):
for j in range (3):
self . add_widget ( Button ( font_size =100 ,
on_press = self . boton_presionado , text = ' ' ))
self . turno = 'O '
Cuando se presiona un botón se verifica si la casilla está vacía para poder jugar
en ella. Si no es así se cambia el texto del botón y se cambia el turno para el otro
jugador. Observe que en el método de inicialización de Triqui al texto de todos
los botones se le asigna un espacio.
20.7. Reutilización
Agregamos un botón que tiene propiedades para registrar la fila y la columna,
heredando de la clase Button.
# triqui4 . py
class Boton ( Button ):
fila = NumericProperty (0)
columna = NumericProperty (0)
Esto nos permite pasar el estado de los botones a una matriz con el siguiente mé-
todo de la clase Triqui:
# triqui4 . py
def botones_a_matriz ( self , tablero ):
for i in self . children :
f = i . fila
c = i . columna
self . tablero [ f ][ c ]= i . text
Así podremos reutilizar el módulo validar, creando la matriz que lleva el estado
del juego :
20.7 Reutilización 257
# triqui4 . py
Ahora estamos en condiciones de poner valores a la matriz cada vez que se halla
realizado una jugada:
# triqui5 . py
def boton_presionado ( self , w ):
if w . text != ' ':
MostrarMensaje ( ' Error ! ' , " Ya se ha jugado en esa casilla ! " )
return
else :
if self . turno == 'O ':
w . text = 'O '
self . turno = 'X '
self . botones_a_matriz ()
if gana ( " O " , self . tablero ):
MostrarMensaje ( " Fin " , " Gana el jugador O " )
else :
# Muy similar para el otro jugador !
258 Triqui con interfaz gráfica
20.8. Reset
Podemos reiniciar el juego cada vez que un jugador gane, mediante la creación del
siguiente método de Triqui:
# triqui6 . py
def reset ( self ):
for i in self . children :
i . text = ' '
self . turno = 'O '
Ahora lo llamamos cada vez que un jugador gana, así el tablero de juego quedará
limpio para que se inicie otra partida.
# triqui6 . py
def boton_presionado ( self , w ):
# Todo lo anterior igual
else :
w . text = 'X '
self . turno = 'O '
self . botones_a_matriz ()
if gana ( " X " , self . tablero ):
MostrarMensaje ( " Fin " , " Gana el jugador X " )
self . reset ()
# triqui7 . py
def boton_presionado ( self , w ):
# Todo lo anterior igual
if empate ( self . tablero ):
MostrarMensaje ( " Fin " , " Empate ! " )
self . reset ()
20.9 Reestructurando, otra vez 259
# triqui8 . py
def revisar ( self ):
if gana ( self . turno , self . tablero ):
mensaje = " Gana el jugador " + self . turno + " . "
MostrarMensaje ( " Fin " , mensaje )
self . reset ()
elif empate ( self . tablero ):
MostrarMensaje ( " Fin " , " Empate ! " )
self . reset ()
else :
self . turno = self . otro ()
# triqui8 . py
def otro ( self ):
if self . turno == 'O ':
return 'X '
else :
return 'O '
Así terminamos con un programa que tiene en total 4 clases, con 9 métodos distri-
buidos en ellas, además de las 9 funciones del módulo validar. Tiene 96 líneas de
código en triqui9.py y 66 en validar, para un total de 162.
260 Triqui con interfaz gráfica
Ilustra algo que siempre pasa con los programas textuales, cuando se convierten a
gráficos se aumenta el código substancialmente. La ventaja, aparte de la estética,
es que el Triqui con la biblioteca kivy puede ejecutarse en Linux, Windows, Mac
OS X, Android y iOS (el sistema operativo de los teléfonos iphone y de los tablets
ipad).
Por ejemplo, el paquete para Android del triqui puede descargarse de:
http://cic.javerianacali.edu.co/~abecerra/files/KivyTriquiABe-1.1.0-debug.
apk
Para instalarlo en un smartphone o tablet.
20.10. Resumen
triqui0.py: crea una ventana vacía
triqui4.py: Agrega una clase heredada de Button para llevar fila y columna
triqui5.py: Cada vez que se juega se copia el estado de los botones a una matriz
triqui6.py: Se valida si los jugadores ganan el juego con el código del triqui viejo
y se resetea el juego.
20.11. Glosario
Evento: Acción generada por el usuario de una aplicación gráfica.
Widget: Elemento de una aplicación gráfica que se coloca en una ventana. Hay
botones, paneles, barras deslizadoras, áreas de texto, entre otras clases de
widgets.
20.12 Ejercicios 261
20.12. Ejercicios
1. Modifique el triqui para que el computador juegue automáticamente, selec-
cionando una casilla vacía al azar.
2. Agregue un sonido cada vez que se haga una jugada válida y otro sonido de
error para cuando el jugador seleccione una casilla en la que ya se ha jugado.
Depuración
Hay diferentes tipos de error que pueden suceder en un programa y es muy útil
distinguirlos a fin de rastrearlos más rápidamente:
263
264 Depuración
4. Asegúrese de que las cadenas en los programas estén encerradas entre comi-
llas.
5. Si usted tiene cadenas multilínea creadas con tres comillas consecutivas, veri-
fique su terminación. Una cadena no terminada puede causar un error deno-
minado invalid token al final de su programa, o puede tratar la siguiente
parte del programa como si fuera una cadena, hasta toparse con la siguiente
A.2 Errores en tiempo de ejecución 265
7. Busque por la confusión clásica entre = y ==, adentro y afuera de los condi-
cionales.
Si hay un ciclo sospechoso de ser la causa del problema, añada una sentencia
print inmediatamente antes del ciclo que diga “entrando al ciclo” y otra
inmediatamente después, que diga “saliendo del ciclo.”
Ejecute el programa. Si obtiene el primer mensaje y no obtiene el segundo,
ha encontrado su ciclo infinito. revise la sección posterior “Ciclo Infinito”.
Si ninguno de estos pasos funciona, revise otros ciclos y otras funciones re-
cursivas, o métodos.
Si usted cree que tiene un ciclo infinito añada una sentencia print al final de éste
que imprima los valores de las variables de ciclo (las que aparecen en la condición)
y el valor de la condición.
A.2 Errores en tiempo de ejecución 267
Por ejemplo:
Ahora, cuando ejecute el programa, usted verá tres líneas de salida para cada ite-
ración del ciclo. En la última iteración la condición debe ser falsa. Si el ciclo sigue,
usted podrá ver los valores de x y y, y puede deducir por qué no se están actuali-
zando correctamente.
La mayoría de las veces una recursión infinita causará que el programa se ejecu-
te durante un momento y luego producirá un error: Maximum recursion depth
exceeded.
Si sospecha que una función o método está causando una recursión infinita, em-
piece por chequear la existencia de un caso base. En otras palabras, debe haber
una condición que haga que el programa o método retorne sin hacer un llamado
recursivo. Si no lo hay, es necesario reconsiderar el algoritmo e identificar un caso
base.
Si hay un caso base, pero el programa no parece alcanzarlo, añada una sentencia
print al principio de la función o método que imprima los parámetros. Ahora,
cuando ejecute el programa usted verá unas pocas líneas de salida cada vez que la
función o método es llamada, y podrá ver los parámetros. Si no están cambiando
de valor acercándose al caso base, usted podrá deducir por qué ocurre esto.
Ahora, cuando ejecute el programa, se imprimirá una traza de cada función a me-
dida que van siendo llamadas.
NameError: usted está tratando de usar una variable que no existe en el ambiente
actual. Recuerde que las variables locales son locales. No es posible referirse
a ellas afuera de la función donde se definieron.
IndexError: el índice que está usando para acceder a una lista, cadena o tupla es
más grande que su longitud menos uno. Inmediatamente, antes de la línea
del error, agregue una sentencia print para desplegar el valor del índice y la
longitud del arreglo. ¿Tiene éste el tamaño correcto? ¿Tiene el índice el valor
correcto?
Uno de los problemas de usar sentencias print para depurar es que uno puede
terminar inundado de salida. Hay dos formas de proceder: simplificar la salida o
simplificar el programa.
Para simplificar la salida se pueden eliminar o comentar sentencias print que no
son de ayuda, o se pueden combinar, o se puede dar formato a la salida, de forma
que quede más fácil de entender.
Para simplificar el programa hay varias cosas que se pueden hacer. Primero, dis-
minuya la escala del problema que el programa intenta resolver. Por ejemplo, si
usted está ordenando un arreglo, utilice uno pequeño como entrada. Si el programa
toma entrada del usuario, pásele la entrada más simple que causa el problema.
Segundo, limpie el programa. Borre el código muerto y reorganízelo para hacerlo
lo más legible que sea posible. Por ejemplo, si usted sospecha que el problema está
en una sección de código profundamente anidada, intente reescribir esa parte con
una estructura más sencilla. Si sospecha de una función grande, trate de partirla
en funciones mas pequeñas y pruébelas separadamente.
Este proceso de encontrar el caso mínimo de prueba que activa el problema a me-
nudo permite encontrar el error. Si usted encuentra que el programa funciona en
una situación, pero no en otras, esto le da una pista de lo que está sucediendo.
Similarmente, reescribir un trozo de código puede ayudar a encontrar errores muy
sutiles. Si usted hace un cambio que no debería alterar el comportamiento del pro-
grama, y sí lo hace, esto es una señal de alerta.
270 Depuración
¿Hay algo que el programa debería hacer, pero no hace? Encuentre la sección
de código que tiene dicha funcionalidad y asegúrese de que se ejecuta en los
momentos adecuados.
¿Hay una sección de código que produce un efecto que no esperaba usted?
Asegúrese de que entiende dicha sección de código, especialmente si tiene
llamados a funciones o métodos en otros módulos. Lea la documentación
para las funciones que usted llama. Intente escribir casos de prueba más sen-
cillos y chequee los resultados.
Para programar, usted necesita un modelo mental de cómo trabajan los programas.
Si usted escribe un programa que no hace lo que se espera, muy frecuentemente el
problema no está en el programa, sino en su modelo mental.
A.3 Errores semánticos 271
y = x / (2 * math . pi );
Cuando no esté seguro del orden de evaluación, use paréntesis. No sólo corregirá
el programa si había un error, sino que también lo hará mas legible para otras
personas que no se sepan las reglas de precedencia.
se podría escribir:
Frustración e ira.
divague. Algunos de los mejores lugares para encontrar errores son los trenes, du-
chas y la cama, justo antes de dormir.
Si hay un mensaje de error, ¿cuál es, y a qué parte del programa se refiere?
¿Cuál fue el último cambio antes de que se presentara el error? ¿Cuáles fue-
ron las últimas líneas de código que escribió usted o cuál es el nuevo caso de
prueba que falla?
Cuando encuentre el error, tómese un segundo para pensar sobre lo que podría
haber realizado para encontrarlo más rápido. La próxima vez que le ocurra algo
similar, será capaz de encontrar el error rápidamente.
Recuerde, el objetivo no sólo es hacer que el programa funcione. El objetivo es
aprender cómo hacer que los programas funcionen.
274 Depuración
Apéndice B
Lecturas adicionales
recomendadas
¿Así que, hacia adonde ir desde aquí? Hay muchas direcciones para avanzar, ex-
tender su conocimiento de Python específicamente y sobre la ciencia de la compu-
tación en general.
Los ejemplos en este libro han sido deliberadamente sencillos, por esto no han mos-
trado las capacidades más excitantes de Python. Aquí hay una pequeña muestra
de las extensiones de Python y sugerencias de proyectos que las utilizan.
275
276 Lecturas adicionales recomendadas
Aquí hay algunos libros que contienen más material sobre el lenguaje Python:
B.2 Libros generales de ciencias de la computación recomendados 277
The Elements of Java Style, editado por Al Vermeulen, es otro libro pequeño
que discute algunos de los puntos mas sutiles de la buena programación, co-
278 Lecturas adicionales recomendadas
The New Turing Omnibus, de A.K Dewdney, hace una amable introducción a
66 tópicos en ciencias de la computación, que van desde la computación pa-
ralela hasta los virus de computador, desde escanografías hasta algoritmos
genéticos. Todos son cortos e interesantes. Un libro anterior de Dewdney The
Armchair Universe es una colección de artículos de su columna Computer Re-
creations en la revista Scientific American (Investigación y Ciencia), estos libros
son una rica fuente de ideas para emprender proyectos.
Turtles, Termites and Traffic Jams, de Mitchel Resnick, trata sobre el poder de
la descentralización y cómo el comportamiento complejo puede emerger de
la simple actividad coordinada de una multitud de agentes. Introduce el len-
guaje StarLogo que permite escribir programas multiagentes. Los programas
examinados demuestran el comportamiento complejo agregado, que a me-
nudo es contraintuitivo. Muchos de estos programas fueron escritos por es-
tudiantes de colegio y universidad. Programas similares pueden escribirse
en Python usando hilos y gráficos simples.
los temas que trata Hofstadter es el de los “ciclos extraños”, en los que los pa-
trones evolucionan y ascienden hasta que se encuentran a sí mismos otra vez.
La tesis de Hofstadter es que esos “ciclos extraños” son una parte esencial de
lo que separa lo animado de lo inanimado. Él muestra patrones como éstos
en la música de Bach, los cuadros de Escher y el teorema de incompletitud
de Gödel.
280 Lecturas adicionales recomendadas
Apéndice C
Preamble
The purpose of this License is to make a manual, textbook, or other written do-
cument “free” in the sense of freedom: to assure everyone the effective freedom
to copy and redistribute it, with or without modifying it, either commercially or
noncommercially. Secondarily, this License preserves for the author and publis-
her a way to get credit for their work, while not being considered responsible for
modifications made by others.
This License is a kind of “copyleft,” which means that derivative works of the
document must themselves be free in the same sense. It complements the GNU
General Public License, which is a copyleft license designed for free software.
281
282 GNU Free Documentation License
We have designed this License in order to use it for manuals for free software,
because free software needs free documentation: a free program should come with
manuals providing the same freedoms that the software does. But this License is
not limited to software manuals; it can be used for any textual work, regardless of
subject matter or whether it is published as a printed book. We recommend this
License principally for works whose purpose is instruction or reference.
text editors or (for images composed of pixels) generic paint programs or (for dra-
wings) some widely available drawing editor, and that is suitable for input to text
formatters or for automatic translation to a variety of formats suitable for input
to text formatters. A copy made in an otherwise Transparent file format whose
markup has been designed to thwart or discourage subsequent modification by
readers is not Transparent. A copy that is not “Transparent” is called “Opaque.”
Examples of suitable formats for Transparent copies include plain ASCII without
markup, Texinfo input format, LATEX input format, SGML or XML using a publicly
available DTD, and standard-conforming simple HTML designed for human mo-
dification. Opaque formats include PostScript, PDF, proprietary formats that can
be read and edited only by proprietary word processors, SGML or XML for which
the DTD and/or processing tools are not generally available, and the machine-
generated HTML produced by some word processors for output purposes only.
The “Title Page” means, for a printed book, the title page itself, plus such following
pages as are needed to hold, legibly, the material this License requires to appear
in the title page. For works in formats which do not have any title page as such,
“Title Page” means the text near the most prominent appearance of the work’s title,
preceding the beginning of the body of the text.
You may copy and distribute the Document in any medium, either commercially or
noncommercially, provided that this License, the copyright notices, and the license
notice saying this License applies to the Document are reproduced in all copies,
and that you add no other conditions whatsoever to those of this License. You may
not use technical measures to obstruct or control the reading or further copying
of the copies you make or distribute. However, you may accept compensation in
exchange for copies. If you distribute a large enough number of copies you must
also follow the conditions in Section 3.
You may also lend copies, under the same conditions stated above, and you may
publicly display copies.
284 GNU Free Documentation License
C.4. Modifications
You may copy and distribute a Modified Version of the Document under the con-
ditions of Sections 2 and 3 above, provided that you release the Modified Version
under precisely this License, with the Modified Version filling the role of the Do-
C.4 Modifications 285
Use in the Title Page (and on the covers, if any) a title distinct from that of
the Document, and from those of previous versions (which should, if there
were any, be listed in the History section of the Document). You may use the
same title as a previous version if the original publisher of that version gives
permission.
List on the Title Page, as authors, one or more persons or entities respon-
sible for authorship of the modifications in the Modified Version, together
with at least five of the principal authors of the Document (all of its principal
authors, if it has less than five).
State on the Title page the name of the publisher of the Modified Version, as
the publisher.
Include, immediately after the copyright notices, a license notice giving the
public permission to use the Modified Version under the terms of this Licen-
se, in the form shown in the Addendum below.
Preserve in that license notice the full lists of Invariant Sections and required
Cover Texts given in the Document’s license notice.
Preserve the section entitled “History,” and its title, and add to it an item
stating at least the title, year, new authors, and publisher of the Modified
Version as given on the Title Page. If there is no section entitled “History”
in the Document, create one stating the title, year, authors, and publisher of
the Document as given on its Title Page, then add an item describing the
Modified Version as stated in the previous sentence.
286 GNU Free Documentation License
Preserve the network location, if any, given in the Document for public access
to a Transparent copy of the Document, and likewise the network locations
given in the Document for previous versions it was based on. These may be
placed in the “History” section. You may omit a network location for a work
that was published at least four years before the Document itself, or if the
original publisher of the version it refers to gives permission.
Preserve all the Invariant Sections of the Document, unaltered in their text
and in their titles. Section numbers or the equivalent are not considered part
of the section titles.
Delete any section entitled “Endorsements.” Such a section may not be inclu-
ded in the Modified Version.
If the Modified Version includes new front-matter sections or appendices that qua-
lify as Secondary Sections and contain no material copied from the Document, you
may at your option designate some or all of these sections as invariant. To do this,
add their titles to the list of Invariant Sections in the Modified Version’s license
notice. These titles must be distinct from any other section titles.
You may add a section entitled “Endorsements,” provided it contains nothing but
endorsements of your Modified Version by various parties—for example, state-
ments of peer review or that the text has been approved by an organization as the
authoritative definition of a standard.
You may add a passage of up to five words as a Front-Cover Text, and a passage
of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the
Modified Version. Only one passage of Front-Cover Text and one of Back-Cover
Text may be added by (or through arrangements made by) any one entity. If the
Document already includes a cover text for the same cover, previously added by
you or by arrangement made by the same entity you are acting on behalf of, you
C.5 Combining Documents 287
may not add another; but you may replace the old one, on explicit permission from
the previous publisher that added the old one.
The author(s) and publisher(s) of the Document do not by this License give per-
mission to use their names for publicity for or to assert or imply endorsement of
any Modified Version.
You may extract a single document from such a collection, and distribute it indi-
vidually under this License, provided you insert a copy of this License into the
extracted document, and follow this License in all other respects regarding verba-
tim copying of that document.
A compilation of the Document or its derivatives with other separate and indepen-
dent documents or works, in or on a volume of a storage or distribution medium,
does not as a whole count as a Modified Version of the Document, provided no
compilation copyright is claimed for the compilation. Such a compilation is called
an “aggregate,” and this License does not apply to the other self-contained works
thus compiled with the Document, on account of their being thus compiled, if they
are not themselves derivative works of the Document.
If the Cover Text requirement of Section 3 is applicable to these copies of the Docu-
ment, then if the Document is less than one quarter of the entire aggregate, the Do-
cument’s Cover Texts may be placed on covers that surround only the Document
within the aggregate. Otherwise they must appear on covers around the whole
aggregate.
C.8. Translation
C.9. Termination
You may not copy, modify, sublicense, or distribute the Document except as ex-
pressly provided for under this License. Any other attempt to copy, modify, subli-
cense, or distribute the Document is void, and will automatically terminate your
rights under this License. However, parties who have received copies, or rights,
from you under this License will not have their licenses terminated so long as such
parties remain in full compliance.
the Free Software Foundation; with the Invariant Sections being LIST
THEIR TITLES, with the Front-Cover Texts being LIST, and with the
Back-Cover Texts being LIST. A copy of the license is included in the
section entitled “GNU Free Documentation License.”
Licencia de documentación
libre de GNU
This is an unofficial translation of the GNU Free Documentation License into Spa-
nish. It was not published by the Free Software Foundation, and does not legally
state the distribution terms for documentation that uses the GNU FDL – only the
original English text of the GNU FDL does that. However, we hope that this trans-
lation will help Spanish speakers understand the GNU FDL better.
Esta es una traducción no oficial de la GNU Free Document License a Español
(Castellano). No ha sido publicada por la Free Software Foundation y no establece
legalmente los términos de distribución para trabajos que usen la GFDL (sólo el
texto de la versión original en Inglés de la GFDL lo hace). Sin embargo, esperamos
que esta traducción ayude a los hispanohablantes a entender mejor la GFDL. La
versión original de la GFDL está disponible en la Free Software Foundation[1].
Esta traducción está basada en una la versión 1.1 de Igor Támara y Pablo Reyes.
Sin embargo la responsabilidad de su interpretación es de Joaquín Seoane.
Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc. 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA. Se permite la copia y distribución de copias
literales de este documento de licencia, pero no se permiten cambios[1].
291
292 Licencia de documentación libre de GNU
Preámbulo
El propósito de esta licencia es permitir que un manual, libro de texto, u otro do-
cumento escrito sea libre en el sentido de libertad: asegurar a todo el mundo la
libertad efectiva de copiarlo y redistribuirlo, con o sin modificaciones, de manera
comercial o no. En segundo término, esta licencia proporciona al autor y al edi-
tor[2] una manera de obtener reconocimiento por su trabajo, sin que se le considere
responsable de las modificaciones realizadas por otros.
Esta licencia es de tipo copyleft, lo que significa que los trabajos derivados del do-
cumento deben a su vez ser libres en el mismo sentido. Complementa la Licencia
Pública General de GNU, que es una licencia tipo copyleft diseñada para el soft-
ware libre.
Hemos diseñado esta licencia para usarla en manuales de software libre, ya que
el software libre necesita documentación libre: un programa libre debe venir con
manuales que ofrezcan la mismas libertades que el software. Pero esta licencia no
se limita a manuales de software; puede usarse para cualquier texto, sin tener en
cuenta su temática o si se publica como libro impreso o no. Recomendamos esta
licencia principalmente para trabajos cuyo fin sea instructivo o de referencia.
sonas. Ejemplos de formatos de imagen transparentes son PNG, XCF y JPG. Los
formatos Opacos incluyen formatos propietarios que pueden ser leídos y edita-
dos únicamente en procesadores de palabras propietarios, SGML o XML para los
cuáles las DTD y/o herramientas de procesamiento no estén ampliamente dispo-
nibles, y HTML, PostScript o PDF generados por algunos procesadores de palabras
sólo como salida.
La Portada significa, en un libro impreso, la página de título, más las páginas si-
guientes que sean necesarias para mantener legiblemente el material que esta Li-
cencia requiere en la portada. Para trabajos en formatos que no tienen página de
portada como tal, Portada significa el texto cercano a la aparición más prominente
del título del trabajo, precediendo el comienzo del cuerpo del texto.
Una sección Titulada XYZ significa una parte del Documento cuyo título es preci-
samente XYZ o contiene XYZ entre paréntesis, a continuación de texto que traduce
XYZ a otro idioma (aquí XYZ se refiere a nombres de sección específicos mencio-
nados más abajo, como Agradecimientos, Dedicatorias , Aprobaciones o Historia.
Conservar el Título de tal sección cuando se modifica el Documento significa que
permanece una sección Titulada XYZ según esta definición .
El Documento puede incluir Limitaciones de Garantía cercanas a la nota donde se
declara que al Documento se le aplica esta Licencia. Se considera que estas Limi-
taciones de Garantía están incluidas, por referencia, en la Licencia, pero sólo en
cuanto a limitaciones de garantía: cualquier otra implicación que estas Limitacio-
nes de Garantía puedan tener es nula y no tiene efecto en el significado de esta
Licencia.
D.4. Modificaciones
Puede copiar y distribuir una Versión Modificada del Documento bajo las condi-
ciones de las secciones 2 y 3 anteriores, siempre que usted libere la Versión Mo-
dificada bajo esta misma Licencia, con la Versión Modificada haciendo el rol del
Documento, por lo tanto dando licencia de distribución y modificación de la Ver-
sión Modificada a quienquiera posea una copia de la misma. Además, debe hacer
lo siguiente en la Versión Modificada:
Usar en la Portada (y en las cubiertas, si hay alguna) un título distinto al
del Documento y de sus versiones anteriores (que deberían, si hay alguna,
estar listadas en la sección de Historia del Documento). Puede usar el mismo
título de versiones anteriores al original siempre y cuando quien las publicó
originalmente otorgue permiso.
Conservar todas las Secciones Invariantes del Documento, sin alterar su texto
ni sus títulos. Números de sección o el equivalente no son considerados parte
de los títulos de la sección.
la Versión Modificada. Tales títulos deben ser distintos de cualquier otro título de
sección.
Puede añadir una sección titulada Aprobaciones, siempre que contenga únicamen-
te aprobaciones de su Versión Modificada por otras fuentes –por ejemplo, obser-
vaciones de peritos o que el texto ha sido aprobado por una organización como la
definición oficial de un estándar.
Puede añadir un pasaje de hasta cinco palabras como Texto de Cubierta Delantera
y un pasaje de hasta 25 palabras como Texto de Cubierta Trasera en la Versión
Modificada. Una entidad solo puede añadir (o hacer que se añada) un pasaje al
Texto de Cubierta Delantera y uno al de Cubierta Trasera. Si el Documento ya
incluye un textos de cubiertas añadidos previamente por usted o por la misma
entidad que usted representa, usted no puede añadir otro; pero puede reemplazar
el anterior, con permiso explícito del editor que agregó el texto anterior.
Con esta Licencia ni los autores ni los editores del Documento dan permiso pa-
ra usar sus nombres para publicidad ni para asegurar o implicar aprobación de
cualquier Versión Modificada.
Puede hacer una colección que conste del Documento y de otros documentos libe-
rados bajo esta Licencia, y reemplazar las copias individuales de esta Licencia en
todos los documentos por una sola copia que esté incluida en la colección, siem-
pre que siga las reglas de esta Licencia para cada copia literal de cada uno de los
documentos en cualquiera de los demás aspectos.
Puede extraer un solo documento de una de tales colecciones y distribuirlo indivi-
dualmente bajo esta Licencia, siempre que inserte una copia de esta Licencia en el
documento extraído, y siga esta Licencia en todos los demás aspectos relativos a la
copia literal de dicho documento.
Una recopilación que conste del Documento o sus derivados y de otros documen-
tos o trabajos separados e independientes, en cualquier soporte de almacenamien-
to o distribución, se denomina un agregado si el copyright resultante de la compi-
lación no se usa para limitar los derechos de los usuarios de la misma más allá de
lo que los de los trabajos individuales permiten. Cuando el Documento se incluye
en un agregado, esta Licencia no se aplica a otros trabajos del agregado que no
sean en sí mismos derivados del Documento.
Si el requisito de la sección 3 sobre el Texto de Cubierta es aplicable a estas copias
del Documento y el Documento es menor que la mitad del agregado entero, los
Textos de Cubierta del Documento pueden colocarse en cubiertas que enmarquen
solamente el Documento dentro del agregado, o el equivalente electrónico de las
cubiertas si el documento está en forma electrónica. En caso contrario deben apa-
recer en cubiertas impresas enmarcando todo el agregado.
300 Licencia de documentación libre de GNU
D.8. Traducción
La Traducción es considerada como un tipo de modificación, por lo que usted pue-
de distribuir traducciones del Documento bajo los términos de la sección 4. El re-
emplazo de las Secciones Invariantes con traducciones requiere permiso especial
de los dueños de derecho de autor, pero usted puede añadir traducciones de al-
gunas o todas las Secciones Invariantes a las versiones originales de las mismas.
Puede incluir una traducción de esta Licencia, de todas las notas de licencia del
documento, así como de las Limitaciones de Garantía, siempre que incluya tam-
bién la versión en Inglés de esta Licencia y las versiones originales de las notas de
licencia y Limitaciones de Garantía. En caso de desacuerdo entre la traducción y
la versión original en Inglés de esta Licencia, la nota de licencia o la limitación de
garantía, la versión original en Inglés prevalecerá.
Si una sección del Documento está Titulada Agradecimientos, Dedicatorias o His-
toria el requisito (sección 4) de Conservar su Título (Sección 1) requerirá, típica-
mente, cambiar su título.
D.9. Terminación
Usted no puede copiar, modificar, sublicenciar o distribuir el Documento salvo
por lo permitido expresamente por esta Licencia. Cualquier otro intento de copia,
modificación, sublicenciamiento o distribución del Documento es nulo, y dará por
terminados automáticamente sus derechos bajo esa Licencia. Sin embargo, los ter-
ceros que hayan recibido copias, o derechos, de usted bajo esta Licencia no verán
terminadas sus licencias, siempre que permanezcan en total conformidad con ella.
siendo las Secciones Invariantes LISTE SUS TÍTULOS, siendo los Textos
de Cubierta Delantera LISTAR, y siendo sus Textos de Cubierta Trasera
LISTAR.
Notas
[1] Ésta es la traducción del Copyright de la Licencia, no es el Copyright de esta
traducción no autorizada.
[2] La licencia original dice publisher, que es, estrictamente, quien publica, diferen-
te de editor, que es más bien quien prepara un texto para publicar. En castellano
editor se usa para ambas cosas.
[3] En sentido estricto esta licencia parece exigir que los títulos sean exactamente
Acknowledgements, Dedications, Endorsements e History, en inglés.
Índice alfabético
índice, 102, 110, 129, 157, 268 asignación de tuplas, 146, 233
negativo, 102 asignación múltiple, 85, 98
atributo, 192
acceso, 116
de clase, 227, 234
acertijo, 1, 12
atributo de clase, 227, 234
acumulador, 231, 234, 246
atributos, 184
algoritmo, 23, 199, 200
AttributeError, 269
alias, 124, 129
Ambigüedad, 20 barajar, 232
ambigüedad, 186 bloque, 58, 67
análisis sintáctico, 23 borrado
analizar sintácticamente, 19 en listas, 122
andamiaje, 71, 83 borrado en listas, 122
anidamiento, 67
archivo, 180 código de fuente, 23
texto, 172 código de objeto, 23
archivo de texto, 172, 180 código ejecutable, 23
archivos, 169 código muerto, 70, 83
argumento, 39, 47, 52 cadena, 27, 28
Asignación inmutable, 106
de tuplas, 146 longitud, 102
asignación, 28, 37, 85 segmento, 104
de tupla, 154 cadena de formato, 173, 180
de tuplas, 233 cadena inmutable, 106
múltiple, 98 caja, 163
asignación de alias, 160 caja de función, 163
asignación de tupla, 154 carácter, 101
303
304 ÍNDICE ALFABÉTICO
unidad, 23
uso de alias, 190