Piensa en Python: 2ª Edición
Piensa en Python: 2ª Edición
Piensa en Python
Cómo pensar como un científico informático
2.ª edición, versión 2.4.0
Machine Translated by Google
Machine Translated by Google
Piensa en Python
Cómo pensar como un científico informático
2.ª edición, versión 2.4.0
allen downey
Prensa de té verde
Needham, Massachusetts
Machine Translated by Google
Derechos de autor © 2015 Allen Downey.
Prensa de té verde
9 Washburn Ave
Needham MA 02492
Se otorga permiso para copiar, distribuir y/o modificar este documento bajo los términos de la licencia Creative Commons
AttributionNonCommercial 3.0 Unported, que está disponible en
.
La forma original de este libro es el código fuente de LATEX. La compilación de esta fuente LATEX tiene el efecto de
generar una representación independiente del dispositivo de un libro de texto, que se puede convertir a otros formatos e
imprimir.
La fuente LATEX para este libro está disponible en
Machine Translated by Google
Prefacio
La extraña historia de este libro.
En enero de 1999 me estaba preparando para dar una clase de introducción a la programación en Java. Lo
había enseñado tres veces y me estaba frustrando. La tasa de fracaso en la clase era demasiado alta e, incluso
para los estudiantes que tenían éxito, el nivel general de rendimiento era demasiado bajo.
Uno de los problemas que vi fueron los libros. Eran demasiado grandes, con demasiados detalles innecesarios
sobre Java y sin suficiente orientación de alto nivel sobre cómo programar. Y todos sufrieron el efecto de la
trampilla: comenzarían con facilidad, avanzarían gradualmente y luego, en algún lugar alrededor del Capítulo
5, se desmoronarían. Los estudiantes obtendrían demasiado material nuevo, demasiado rápido, y yo pasaría
el resto del semestre recogiendo las piezas.
Dos semanas antes del primer día de clases, decidí escribir mi propio libro. Mis objetivos eran:
• Que sea breve. Es mejor para los estudiantes leer 10 páginas que no leer 50 páginas.
• Tenga cuidado con el vocabulario. Traté de minimizar la jerga y definir cada término al principio
usar.
• Construya gradualmente. Para evitar trampas, tomé los temas más difíciles y los dividí en una serie de
pequeños pasos.
• Concéntrese en la programación, no en el lenguaje de programación. Incluí el subconjunto útil mínimo de
Java y omití el resto.
Necesitaba un título, así que por capricho elegí Cómo pensar como un científico informático.
Mi primera versión fue tosca, pero funcionó. Los estudiantes leyeron y entendieron lo suficiente como para que
yo pudiera dedicar tiempo de clase a los temas difíciles, los temas interesantes y (lo más importante) dejar que
los estudiantes practicaran.
Lancé el libro bajo la Licencia de Documentación Libre GNU, que permite a los usuarios copiar, modificar y
distribuir el libro.
Lo que sucedió después es la parte interesante. Jeff Elkner, profesor de secundaria en Virginia, adoptó mi libro
y lo tradujo a Python. Me envió una copia de su traducción y tuve la experiencia inusual de aprender Python
leyendo mi propio libro. Como Green Tea Press, publiqué la primera versión de Python en 2001.
En 2003 comencé a enseñar en Olin College y pude enseñar Python por primera vez. El contraste con Java
fue sorprendente. Los estudiantes lucharon menos, aprendieron más, trabajaron en proyectos más interesantes
y, en general, se divirtieron mucho más.
Machine Translated by Google
vi Capítulo 0. Prefacio
Desde entonces he seguido desarrollando el libro, corrigiendo errores, mejorando algunos de los ejemplos y
añadiendo material, especialmente ejercicios.
El resultado es este libro, ahora con el título menos grandioso Think Python. Algunos de los cambios
son:
• Agregué una sección sobre depuración al final de cada capítulo. Estas secciones presentan técnicas
generales para encontrar y evitar errores, y advertencias sobre caídas en boxes de Python.
• Agregué más ejercicios, desde pruebas breves de comprensión hasta algunos proyectos sustanciales.
La mayoría de los ejercicios incluyen un enlace a mi solución.
• Agregué una serie de estudios de casos: ejemplos más largos con ejercicios, soluciones y dis
cusión
• Amplié la discusión de planes de desarrollo de programas y patrones de diseño básicos.
• Agregué apéndices sobre depuración y análisis de algoritmos.
La segunda edición de Think Python tiene estas nuevas características:
• El libro y todo el código de soporte se han actualizado a Python 3.
• Agregué algunas secciones y más detalles en la web para ayudar a los principiantes a comenzar a
ejecutar Python en un navegador, para que no tenga que lidiar con la instalación de Python hasta que
lo desee.
• Para el Capítulo 4.1, cambié de mi propio paquete de gráficos Turtle, llamado Swampy, a uno que es
módulo de Python más estándar, , más fácil de instalar y más potente.
• Agregué un nuevo capítulo llamado "The Goodies", que presenta algunos
Funciones de Python que no son estrictamente necesarias, pero a veces útiles.
Espero que disfrutes trabajando con este libro y que te ayude a aprender a programar y pensar como un
informático, al menos un poco.
Allen B Downey
Colegio Olín
Expresiones de gratitud
Muchas gracias a Jeff Elkner, quien tradujo mi libro de Java a Python, lo que puso en marcha este proyecto y
me presentó lo que resultó ser mi lenguaje favorito.
Gracias también a Chris Meyers, quien contribuyó con varias secciones a Cómo pensar como un científico
informático.
Gracias a la Free Software Foundation por desarrollar la licencia de documentación libre de GNU, que me
ayudó a hacer posible mi colaboración con Jeff y Chris, y a Creative Commons por la licencia que estoy
usando ahora.
Machine Translated by Google
viii
Gracias a los editores de Lulu que trabajaron en Cómo pensar como un científico informático.
Gracias a los editores de O'Reilly Media que trabajaron en Think Python.
Gracias a todos los estudiantes que trabajaron con versiones anteriores de este libro y a todos los
colaboradores (mencionados a continuación) que enviaron correcciones y sugerencias.
Lista de colaboradores
Más de 100 lectores perspicaces y reflexivos han enviado sugerencias y correcciones en los últimos años.
Sus contribuciones y entusiasmo por este proyecto han sido de gran ayuda.
Si tiene una sugerencia o corrección, envíe un correo electrónico a Si .
realizo un cambio en función de sus comentarios, lo agregaré a la lista de colaboradores (a menos que
solicite ser omitido).
Si incluye al menos parte de la oración en la que aparece el error, eso me facilita la búsqueda. Los números
de página y sección también están bien, pero no es tan fácil trabajar con ellos. ¡Gracias!
• Lloyd Hugh Allen envió una corrección a la Sección 8.4.
• Yvon Boulianne envió una corrección de un error semántico en el Capítulo 5.
• Fred Bremmer presentó una corrección en la Sección 2.1.
• Jonah Cohen escribió los scripts de Perl para convertir el código fuente de LaTeX de este libro en hermosos
HTML.
• Michael Conlon envió una corrección gramatical en el Capítulo 2 y una mejora en el estilo en
Capítulo 1, e inició la discusión sobre los aspectos técnicos de los intérpretes.
• Benoît Girard envió una corrección a un error humorístico en la Sección 5.6.
,
• Courtney Gleason y Katherine Smith escribieron, que se utilizó como estudio
de caso en una versión anterior
del libro. Su programa ahora se puede encontrar en el sitio web.
• Lee Harr envió más correcciones de las que tenemos espacio para enumerar aquí y, de hecho, debería figurar
como uno de los principales editores del texto.
• James Kaylin es un estudiante que usa el texto. Ha presentado numerosas correcciones.
• David Kershaw arregló el roto función en la Sección 3.10.
• Eddie Lam ha enviado numerosas correcciones a los Capítulos 1, 2 y 3. También arregló el Makefile para que
cree un índice la primera vez que se ejecuta y nos ayudó a configurar un esquema de control de versiones.
• ManYong Lee envió una corrección al código de ejemplo en la Sección 2.4.
• David Mayo señaló que la palabra "inconscientemente" en el Capítulo 1 debía cambiarse a
"de modo subconsciente".
• Chris McAloon envió varias correcciones a las Secciones 3.9 y 3.10.
• Matthew J. Moelter ha sido colaborador durante mucho tiempo y envió numerosas correcciones y
sugerencias al libro.
Machine Translated by Google
viii Capítulo 0. Prefacio
• Simon Dicon Montford informó que faltaba una definición de función y varios errores tipográficos en el Capítulo 3.
También encontró errores en el función en el Capítulo 13.
• John Ouzts corrigió la definición de "valor de retorno" en el Capítulo 3.
• Kevin Parks envió valiosos comentarios y sugerencias sobre cómo mejorar la distribución
del libro.
• David Pool envió un error tipográfico en el glosario del Capítulo 1, así como amables palabras de aliento.
• Michael Schmitt envió una corrección al capítulo sobre archivos y excepciones.
• Robin Shaw señaló un error en la Sección 13.1, donde la función printTime se usó en un
ejemplo sin ser definido.
• Paul Sleigh encontró un error en el Capítulo 7 y un error en el script Perl de Jonah Cohen que genera
HTML de LaTeX.
• Craig T. Snydal está probando el texto en un curso en la Universidad Drew. Ha aportado varios
valiosas sugerencias y correcciones.
• Ian Thomas y sus alumnos están usando el texto en un curso de programación. Son los primeros en probar los
capítulos de la segunda mitad del libro y han hecho numerosas correcciones y sugerencias.
• Keith Verheyden envió una corrección en el Capítulo 3.
• Peter Winstanley nos hizo saber acerca de un error de larga data en nuestro latín en el Capítulo 3.
• Chris Wrobel hizo correcciones al código en el capítulo sobre E/S de archivos y excepciones.
• Moshe Zadka ha hecho contribuciones invaluables a este proyecto. Además de escribir el primer borrador del
capítulo sobre Diccionarios, brindó orientación continua en las primeras etapas del libro.
• Christoph Zwerschke envió varias correcciones y sugerencias pedagógicas, y explicó el
diferencia entre gleich y selbe.
• James Mayer nos envió una gran cantidad de errores ortográficos y tipográficos, incluidos dos en el
lista de contribuyentes.
• Hayden McAfee detectó una incoherencia potencialmente confusa entre dos ejemplos.
• Ángel Arnal es parte de un equipo internacional de traductores que trabajan en la versión en español de
el texto. También ha encontrado varios errores en la versión en inglés.
• Tauhidul Hoque y Lex Berezhny crearon las ilustraciones del Capítulo 1 y mejoraron muchas
de las otras ilustraciones.
• La Dra. Michele Alzetta detectó un error en el Capítulo 8 y envió algunos comunicados pedagógicos interesantes.
comentarios y sugerencias sobre Fibonacci y Old Maid.
• Andy Mitchell detectó un error tipográfico en el Capítulo 1 y un ejemplo roto en el Capítulo 2.
• Kalin Harvey sugirió una aclaración en el Capítulo 7 y detectó algunos errores tipográficos.
• Christopher P. Smith detectó varios errores tipográficos y nos ayudó a actualizar el libro para Python 2.2.
• David Hutchins detectó un error tipográfico en el Prólogo.
• Gregor Lingl está enseñando Python en una escuela secundaria en Viena, Austria. Él está trabajando en un Ger
man tradujo el libro, y detectó un par de errores graves en el capítulo 5.
Machine Translated by Google
ix
• Julie Peters detectó un error tipográfico en el Prefacio.
• DJ Webre sugirió una aclaración en el Capítulo 3.
• Ken encontró un puñado de errores en los capítulos 8, 9 y 11.
• Ivo Wever detectó un error tipográfico en el Capítulo 5 y sugirió una aclaración en el Capítulo 3.
• Curtis Yanko sugirió una aclaración en el Capítulo 2.
• Ben Logan envió varios errores tipográficos y problemas con la traducción del libro a HTML.
• Jason Armstrong vio la palabra que faltaba en el Capítulo 2.
• Louis Cordier notó un lugar en el Capítulo 16 donde el código no coincidía con el texto.
• Brian Cain sugirió varias aclaraciones en los Capítulos 2 y 3.
• Rob Black envió un montón de correcciones, incluidos algunos cambios para Python 2.2.
• JeanPhilippe Rey de École Centrale Paris envió varios parches, incluidas algunas actualizaciones
para Python 2.2 y otras mejoras bien pensadas.
• Jason Mader de la Universidad George Washington hizo una serie de sugerencias útiles y cor
recciones
• Jan GundtofteBruun nos recordó que “un error” es un error.
• Abel David y Alexis Dinno nos recordaron que el plural de “matrix” es “matrices”, no “matrices”. Este error
estuvo en el libro durante años, pero dos lectores con las mismas iniciales lo reportaron el mismo día. Extraño.
• Charles Thayer nos animó a deshacernos de los puntos y comas que habíamos puesto al final de algunas
declaraciones ya limpiar nuestro uso de "argumento" y "parámetro".
• Roger Sperberg señaló una lógica retorcida en el Capítulo 3.
• Sam Bull señaló un párrafo confuso en el Capítulo 2.
• Andrew Cheung señaló dos instancias de "uso antes de def".
• C. Corey Capel detectó la palabra que faltaba en el Tercer Teorema de Depuración y un error tipográfico en
Capítulo 4.
• Alessandra ayudó a aclarar algunas confusiones sobre las tortugas.
• Wim Champagne encontró un cerebroo en un ejemplo de diccionario.
• Douglas Wright señaló un problema con la división del piso en .
• Jared Spindor encontró algunos desechos al final de una oración.
• Lin Peiheng envió una serie de sugerencias muy útiles.
• Ray Hagtvedt envió dos errores y uno no del todo.
• Torsten Hübsch señaló una inconsistencia en Swampy.
• Inga Petuhhov corrigió un ejemplo en el Capítulo 14.
• Arne Babenhauserheide envió varias correcciones útiles.
Machine Translated by Google
X Capítulo 0. Prefacio
• Mark E. Casida es bueno para detectar palabras repetidas.
• Scott Tyler llenó uno que faltaba. Y luego envió un montón de correcciones.
• Gordon Shephard envió varias correcciones, todas en correos electrónicos separados.
• Andrés Turner ted un error en el Capítulo 8.
• Adam Hobart solucionó un problema con la división del piso en .
• Daryl Hammond y Sarah Zimmerman señalaron que expuse que Zim detectó un error demasiado temprano. Y
tipográfico.
• George Sass encontró un error en una sección de Depuración.
• Brian Bingham sugirió el Ejercicio 11.5.
• Leah EngelbertFenton señaló que usé como nombre de variable, contrariamente a mi propio
consejo. Y luego encontré un montón de errores tipográficos y un "uso antes de la definición".
• Joe Funke detectó un error tipográfico.
• Chaochao Chen encontró una inconsistencia en el ejemplo de Fibonacci.
• Jeff Paine conoce la diferencia entre espacio y spam.
• Lubos Pintes envió un error tipográfico.
• Gregg Lind y Abigail Heithoff sugirieron el ejercicio 14.3.
• Max Hailperin ha enviado una serie de correcciones y sugerencias. Max es uno de los autores de las extraordinarias
Abstracciones Concretas, que tal vez quieras leer cuando termines con este libro.
• Chotipat Pornavalai encontró un error en un mensaje de error.
• Stanislaw Antol envió una lista de sugerencias muy útiles.
• Eric Pashman envió varias correcciones para los capítulos 4–11.
• Miguel Azevedo encontró algunos errores tipográficos.
• Jianhua Liu envió una larga lista de correcciones.
• Nick King encontró una palabra que faltaba.
• Martin Zuther envió una larga lista de sugerencias.
• Adam Zimmerman encontró una inconsistencia en mi caso de una "instancia" y varios otros
errores
• Ratnakar Tiwari sugirió una nota al pie que explica los triángulos degenerados.
• Anurag Goel sugirió otra solución para y envió algunas correcciones adicionales
ciones. Y sabe cómo deletrear a Jane Austen.
• Kelli Kratzer detectó uno de los errores tipográficos.
• Mark Griffiths señaló un ejemplo confuso en el Capítulo 3.
• Roydan Ongie encontró un error en mi método de Newton.
• Patryk Wolowiec me ayudó con un problema en la versión HTML.
Machine Translated by Google
xi
• Mark Chonofsky me habló de una nueva palabra clave en Python 3.
• Russell Coleman me ayudó con mi geometría.
• Nam Nguyen encontró un error tipográfico y señaló que usé el patrón Decorator pero no los hombres.
ción por su nombre.
• Stéphane Morin envió varias correcciones y sugerencias.
• Paul Stoop corrigió un error tipográfico en .
• Eric Bronner señaló una confusión en la discusión del orden de las operaciones.
• Alexandros Gezerlis estableció un nuevo estándar para el número y la calidad de las sugerencias que presentó.
¡Estamos profundamente agradecidos!
• Gray Thomas conoce su derecha de su izquierda.
• Giovanni Escobar Sosa envió una larga lista de correcciones y sugerencias.
• Daniel Neilson corrigió un error sobre el orden de las operaciones.
• Will McGinnis señaló que se definió de manera diferente en dos lugares.
• Frank Hecker señaló un ejercicio que estaba subespecificado y algunos enlaces rotos.
• Animesh B me ayudó a aclarar un ejemplo confuso.
• Martin Caspersen encontró dos errores de redondeo.
• Gregor Ulm envió varias correcciones y sugerencias.
• Dimitrios Tsirigkas me sugirió aclarar un ejercicio.
• Carlos Tafur envió una página de correcciones y sugerencias.
• Martin Nordsletten encontró un error en la solución de un ejercicio.
• Sven Hoexter señaló que una variable llamada sombrea una función incorporada.
• Stephen Gregory señaló el problema con en Phyton 3.
• Ishwar Bhat corrigió mi declaración del último teorema de Fermat.
• Andrea Zanella tradujo el libro al italiano y envió una serie de correcciones a lo largo del
forma.
• Muchas, muchas gracias a Melissa Lewis y Luciano Ramalho por sus excelentes comentarios y sugerencias sobre la
segunda edición.
• Gracias a Harry Percival de PythonAnywhere por su ayuda para que las personas comenzaran a ejecutar Python en un
navegador.
• Xavier Van Aubel hizo varias correcciones útiles en la segunda edición.
• William Murray corrigió mi definición de división de piso.
• Per Starbäck me puso al día sobre saltos de línea universales en Python 3.
• Laurent Rosenfeld y Mihaela Rotaru tradujeron este libro al francés. En el camino, ellos
enviado muchas correcciones y sugerencias.
Además, las personas que detectaron errores tipográficos o hicieron correcciones incluyen a Czeslaw Czapla, Dale
Wil son, Francesco Carlo Cimini, Richard Fursa, Brian McGhie, Lokesh Kumar Makani, Matthew Shultz, Viet Le, Victor
Simeone, Lars OD Christensen, Swarup Sahoo, Alix Etienne , Kuang He, Wei Huang, Karen Barber y Eric Ransom.
Machine Translated by Google
xi Capítulo 0. Prefacio
Machine Translated by Google
Contenido
Prefacio v
1 El camino del programa 1
2 Variables, expresiones y sentencias 9
xiv Contenido
3 funciones 17
3.4 Adición de nuevas funciones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.7 Parámetros y argumentos. . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.10 Funciones fructíferas y funciones nulas. . . . . . . . . . . . . . . . . . . . . . 24
4 Estudio de caso: diseño de interfaz 29
Contenido XV
5 Condicionales y recursión 39
5.9 Diagramas de pila para funciones recursivas. . . . . . . . . . . . . . . . . . . . . 44
6 funciones fructíferas 51
xvi Contenido
7 Iteración 63
8 cuerdas 71
8.1 Una cadena es una secuencia. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
9 Estudio de caso: juego de palabras 83
Contenido xvii
10 listas 89
11 diccionarios 103
11.2 Diccionario como colección de contadores. . . . . . . . . . . . . . . . . . . . . . 104
xviii Contenido
12 tuplas 115
13 Estudio de caso: selección de estructura de datos 125
14 archivos 137
Contenido xix
15 Clases y objetos 147
16 Clases y funciones 155
17 Clases y métodos 161
XX Contenido
18 Herencia 171
19 Las golosinas 183
Contenido xxx
Una depuración 193
B Análisis de Algoritmos 201
B.2 Análisis de las operaciones básicas de Python. . . . . . . . . . . . . . . . . . . . . . 204
XXII Contenido
Machine Translated by Google
Capítulo 1
El camino del programa
El objetivo de este libro es enseñarte a pensar como un informático. Esta forma de pensar combina
algunas de las mejores características de las matemáticas, la ingeniería y las ciencias naturales.
Al igual que los matemáticos, los informáticos utilizan lenguajes formales para denotar ideas
(específicamente cálculos). Al igual que los ingenieros, diseñan cosas, ensamblan componentes en
sistemas y evalúan las compensaciones entre alternativas. Al igual que los científicos, observan el
comportamiento de sistemas complejos, formulan hipótesis y prueban predicciones.
La habilidad más importante para un informático es la resolución de problemas. La resolución de
problemas significa la capacidad de formular problemas, pensar creativamente en soluciones y expresar
una solución con claridad y precisión. Resulta que el proceso de aprender a programar es una excelente
oportunidad para practicar las habilidades de resolución de problemas. Es por eso que este capítulo se
llama, “El camino del programa”.
En un nivel, aprenderá a programar, una habilidad útil en sí misma. En otro nivel, utilizará la programación
como un medio para un fin. A medida que avanzamos, ese final se hará más claro.
1.1 ¿Qué es un programa?
Un programa es una secuencia de instrucciones que especifica cómo realizar un cálculo. El cálculo
puede ser algo matemático, como resolver un sistema de ecuaciones o encontrar las raíces de un
polinomio, pero también puede ser un cálculo simbólico, como buscar y reemplazar texto en un
documento o algo gráfico, como procesar una imagen o reproduciendo un video
Los detalles se ven diferentes en diferentes idiomas, pero aparecen algunas instrucciones básicas en
casi todos los idiomas:
entrada: obtenga datos del teclado, un archivo, la red o algún otro dispositivo.
salida: Mostrar datos en la pantalla, guardarlos en un archivo, enviarlos a través de la red, etc.
matemáticas: Realice operaciones matemáticas básicas como la suma y la multiplicación.
ejecución condicional: compruebe ciertas condiciones y ejecute el código apropiado.
Machine Translated by Google
2 Capítulo 1. El camino del programa
repetición: Realizar alguna acción repetidamente, generalmente con alguna variación.
Lo creas o no, eso es prácticamente todo lo que hay que hacer. Todos los programas que hayas usado, sin
importar cuán complicados sean, se componen de instrucciones que se parecen mucho a estas.
Entonces, puede pensar en la programación como el proceso de dividir una tarea grande y compleja en
subtareas cada vez más pequeñas hasta que las subtareas sean lo suficientemente simples como para
realizarlas con una de estas instrucciones básicas.
1.2 Ejecutando Python
Uno de los desafíos de comenzar con Python es que es posible que deba instalar Python y el software
relacionado en su computadora. Si está familiarizado con su sistema operativo, y especialmente si se siente
cómodo con la interfaz de línea de comandos, no tendrá problemas para instalar Python. Pero para los
principiantes, puede ser doloroso aprender sobre administración de sistemas y programación al mismo tiempo.
Para evitar ese problema, le recomiendo que comience ejecutando Python en un navegador. Más tarde,
cuando se sienta cómodo con Python, le haré sugerencias para instalar Python en su computadora.
Hay varias páginas web que puede usar para ejecutar Python. Si ya tiene un favorito, continúe y utilícelo. De
lo contrario, recomiendo PythonAnywhere. Proporciono instrucciones detalladas para comenzar en
.
Hay dos versiones de Python, llamadas Python 2 y Python 3. Son muy similares, así que si aprendes una, es
fácil cambiar a la otra. De hecho, solo hay algunas diferencias que encontrará como principiante. Este libro
está escrito para Python 3, pero incluyo algunas notas sobre Python 2.
El intérprete de Python es un programa que lee y ejecuta código de Python. Según su entorno, puede iniciar el
intérprete haciendo clic en un icono o escribiendo
en una línea de comando. Cuando se inicia, debería ver un resultado como este:
Las primeras tres líneas contienen información sobre el intérprete y el sistema operativo en el que se ejecuta,
por lo que podría ser diferente para usted. Pero debe verificar que el número de versión, que se encuentra en
este ejemplo, comience con 3, lo que indica que está ejecutando Python 3. Si comienza con 2, está ejecutando
(lo adivinó) Python 2.
La última línea es un mensaje que indica que el intérprete está listo para que ingrese el código. Si escribe una
línea de código y pulsa Intro, el intérprete muestra el resultado:
Ahora estás listo para empezar. De aquí en adelante, asumo que sabe cómo iniciar el intérprete de Python y
ejecutar el código.
Machine Translated by Google
1.3. el primer programa 3
1.3 El primer programa
Tradicionalmente, el primer programa que escribes en un nuevo idioma se llama "¡Hola, mundo!" porque
todo lo que hace es mostrar las palabras "¡Hola, mundo!". En Python, se ve así:
Este es un ejemplo de una declaración de impresión, aunque en realidad no imprime nada en papel. Muestra
un resultado en la pantalla. En este caso, el resultado son las palabras.
Las comillas en el programa marcan el principio y el final del texto a mostrar; no aparecen en el resultado.
Los paréntesis indican que es una función Llegaremos a las funciones en el Capítulo 3.
En Python 2, la declaración de impresión es ligeramente diferente; no es una función, por lo que no usa
paréntesis.
Esta distinción tendrá más sentido pronto, pero eso es suficiente para comenzar.
1.4 Operadores aritméticos
Después de “Hello, World”, el siguiente paso es la aritmética. Python proporciona operadores, que son
símbolos especiales que representan cálculos como la suma y la multiplicación.
Los operadores, , y realizar sumas, restas y multiplicaciones, como en la siguiente
dando ejemplos:
El operador realiza la división:
Finalmente, el operador realiza la exponenciación; es decir, eleva un número a una potencia:
En algunos otros lenguajes, se usa para la exponenciación, pero en Python es un operador bit a bit llamado
XOR. Si no estás familiarizado con los operadores bit a bit, el resultado te sorprenderá:
No cubriré los operadores bit a bit en este libro, pero puede leer sobre ellos en
.
Machine Translated by Google
4 Capítulo 1. El camino del programa
1.5 Valores y tipos
Un valor es una de las cosas básicas con las que trabaja un programa, como una letra o un número. Algunos
valores que hemos visto hasta ahora son, ,y .
Estos valores pertenecen a diferentes tipos: es un número es un número de punto flotante, y
entero, es una cadena, se llama así porque las letras que contiene están encadenadas.
Si no está seguro de qué tipo tiene un valor, el intérprete puede decirle:
En estos resultados, la palabra “clase” se utiliza en el sentido de una categoría; un tipo es una categoría de
valores.
¿Qué pasa con valores como y ? Parecen números, pero están entre comillas.
marcas como cadenas?
Son cuerdas.
Cuando escribe un número entero grande, puede sentirse tentado a usar comas entre grupos de dígitos,
como en . Este no es un número entero legal en Python, pero es legal:
¡Eso no es lo que esperábamos en absoluto! Python interpreta como una secuencia de enteros separados por
comas. Aprenderemos más sobre este tipo de secuencia más adelante.
1.6 Lenguajes formales y naturales
Los lenguajes naturales son los idiomas que hablan las personas, como el inglés, el español y el francés.
No fueron diseñados por personas (aunque la gente trata de imponerles algún orden);
evolucionaron naturalmente.
Los lenguajes formales son lenguajes diseñados por personas para aplicaciones específicas.
Por ejemplo, la notación que usan los matemáticos es un lenguaje formal que es particularmente
bueno para denotar relaciones entre números y símbolos. Los químicos usan un lenguaje formal
para representar la estructura química de las moléculas. Y más importante:
Los lenguajes de programación son lenguajes formales que han sido diseñados para
expresar cálculos.
Machine Translated by Google
1.6. lenguajes formales y naturales 5
Los lenguajes formales tienden a tener reglas de sintaxis estrictas que rigen la estructura de las declaraciones.
Por ejemplo, en matemáticas, la declaración 3 + 3 = 6 tiene una sintaxis correcta, pero 3+ = 3$6 no. En
química, H2O es una fórmula sintácticamente correcta, pero 2Zz no lo es.
Las reglas de sintaxis vienen en dos sabores, pertenecientes a tokens y estructura. Los tokens son los
elementos básicos del lenguaje, como palabras, números y elementos químicos. Uno de los problemas con 3+
= 3$6 es que $ no es un token legal en matemáticas (al menos que yo sepa). Del mismo modo, 2Zz no es
legal porque no hay ningún elemento con la abreviatura Zz.
El segundo tipo de regla de sintaxis se refiere a la forma en que se combinan los tokens. La ecuación 3 + /3
es ilegal porque aunque + y / son fichas legales, no puedes tener una después de la otra. De manera similar,
en una fórmula química, el subíndice viene después del nombre del elemento,
no antes.
Esta es una oración en inglés bien estructurada con tokens no válidos. Esta oración tiene todos los tokens
válidos, pero una estructura no válida.
Cuando lees una oración en inglés o una declaración en un lenguaje formal, tienes que descifrar la estructura
(aunque en un lenguaje natural lo haces inconscientemente). Este proceso se denomina análisis.
Aunque los lenguajes formales y naturales tienen muchas características en común (tokens, estructura y
sintaxis), existen algunas diferencias:
Ambigüedad: Los lenguajes naturales están llenos de ambigüedad, que las personas manejan usando pistas
contextuales y otra información. Los lenguajes formales están diseñados para ser casi o completamente
inequívocos, lo que significa que cualquier declaración tiene exactamente un significado,
independientemente del contexto.
redundancia: para compensar la ambigüedad y reducir los malentendidos, los lenguajes naturales emplean
mucha redundancia. Como resultado, a menudo son detallados. Los lenguajes formales son menos
redundantes y más concisos.
literalidad: Los lenguajes naturales están llenos de modismos y metáforas. Si digo, “Se cayó el centavo”,
probablemente no haya ningún centavo ni nada cayendo (esta expresión significa que alguien entendió
algo después de un período de confusión). Los lenguajes formales significan exactamente lo que dicen.
Debido a que todos crecemos hablando idiomas naturales, a veces es difícil adaptarse a los idiomas formales.
La diferencia entre lenguaje formal y natural es como la diferencia entre poesía y prosa, pero más:
Poesía: Las palabras se usan tanto por sus sonidos como por su significado, y todo el poema en conjunto crea
un efecto o una respuesta emocional. La ambigüedad no solo es común, sino que a menudo es
deliberada.
Prosa: El significado literal de las palabras es más importante, y la estructura aporta más significado. La prosa
es más susceptible de análisis que la poesía, pero sigue siendo a menudo ambigua.
Programas: El significado de un programa de computadora es inequívoco y literal, y puede ser
entendido completamente por el análisis de los tokens y la estructura.
Machine Translated by Google
6 Capítulo 1. El camino del programa
Los lenguajes formales son más densos que los lenguajes naturales, por lo que lleva más tiempo leerlos.
Además, la estructura es importante, por lo que no siempre es mejor leer de arriba a abajo, de izquierda a
derecha. En su lugar, aprenda a analizar el programa en su cabeza, identificando las fichas e interpretando la
estructura. Finalmente, los detalles importan. Pequeños errores de ortografía y puntuación, que puedes evitar en
los lenguajes naturales, pueden marcar una gran diferencia en un lenguaje formal.
1.7 Depuración
Los programadores cometen errores. Por caprichosas razones, los errores de programación se denominan bugs
y el proceso de rastrearlos se denomina depuración.
La programación, y especialmente la depuración, a veces provoca emociones fuertes. Si está luchando con un
error difícil, puede sentirse enojado, abatido o avergonzado.
Existe evidencia de que las personas responden naturalmente a las computadoras como si fueran personas.
Cuando funcionan bien, pensamos en ellos como compañeros de equipo, y cuando son obstinados o groseros,
les respondemos de la misma manera que respondemos a las personas groseras y obstinadas (Reeves y Nass,
The Media Equation: How People Treat Computers, Television, y nuevos medios como personas y lugares reales).
Prepararse para estas reacciones podría ayudarlo a lidiar con ellas. Un enfoque es pensar en la computadora
como un empleado con ciertas fortalezas, como velocidad y precisión, y debilidades particulares, como falta de
empatía e incapacidad para comprender el panorama general.
Tu trabajo es ser un buen gerente: encuentra formas de aprovechar las fortalezas y mitigar las debilidades. Y
encuentre formas de usar sus emociones para abordar el problema, sin dejar que sus reacciones interfieran con
su capacidad para trabajar de manera efectiva.
Aprender a depurar puede ser frustrante, pero es una habilidad valiosa que es útil para muchas actividades más
allá de la programación. Al final de cada capítulo hay una sección, como esta, con mis sugerencias para la
depuración. ¡Espero que ayuden!
1.8 Glosario
resolución de problemas: El proceso de formular un problema, encontrar una solución y expresar
ing eso.
lenguaje de alto nivel: Un lenguaje de programación como Python que está diseñado para ser fácil para
humanos a leer y escribir.
lenguaje de bajo nivel: un lenguaje de programación que está diseñado para que una computadora pueda
ejecutarlo fácilmente; también llamado “lenguaje de máquina” o “lenguaje ensamblador”.
portabilidad: Una propiedad de un programa que puede ejecutarse en más de un tipo de computadora.
Intérprete: Un programa que lee otro programa y lo ejecuta.
indicador: Caracteres que muestra el intérprete para indicar que está listo para tomar entrada
del usuario
programa: Un conjunto de instrucciones que especifica un cálculo.
Machine Translated by Google
1.9. Ejercicios 7
declaración de impresión: una instrucción que hace que el intérprete de Python muestre un valor en
la pantalla.
operador: un símbolo especial que representa un cálculo simple como la suma, la multiplicación o la concatenación
de cadenas.
valor: una de las unidades básicas de datos, como un número o una cadena, que manipula un programa.
tipo: Una categoría de valores. Los tipos que hemos visto hasta ahora son enteros (tipo) y cadenas ), flotante
números de punto (tipo (tipo).
entero: Un tipo que representa números enteros.
punto flotante: un tipo que representa números con partes fraccionarias.
cadena: un tipo que representa secuencias de caracteres.
lenguaje natural: Cualquiera de los lenguajes que habla la gente que evolucionó naturalmente.
lenguaje formal: Cualquiera de los lenguajes que la gente ha diseñado para propósitos específicos, como representar
ideas matemáticas o programas de computadora; todos los lenguajes de programación son lenguajes formales.
token: Uno de los elementos básicos de la estructura sintáctica de un programa, análogo a un
palabra en un lenguaje natural.
sintaxis: Las reglas que gobiernan la estructura de un programa.
analizar: Examinar un programa y analizar la estructura sintáctica.
bug: Un error en un programa.
depuración: El proceso de encontrar y corregir errores.
1.9 Ejercicios
Ejercicio 1.1. Es una buena idea leer este libro frente a una computadora para que pueda probar los ejemplos a medida
que avanza.
Cada vez que esté experimentando con una nueva función, debe intentar cometer errores. Por ejemplo, en el mensaje
"¡Hola, mundo!" programa, ¿qué sucede si omite una de las comillas? ¿Qué pasa si omites ambos? ¿Qué pasa si
escribes mal?
Este tipo de experimento te ayuda a recordar lo que lees; también ayuda cuando está programando, porque llega a
saber qué significan los mensajes de error. Es mejor cometer errores ahora ya propósito que después y sin querer.
1. En una declaración de impresión, ¿qué sucede si omite uno de los paréntesis o ambos?
2. Si está tratando de imprimir una cadena, ¿qué sucede si omite una de las comillas?
¿o ambos?
3. ¿Puedes usar un signo menos para hacer un número negativo como . Que pasa si pones un plus
un signo antes de un número? Qué pasa ?
Machine Translated by Google
8 Capítulo 1. El camino del programa
4. En notación matemática, los ceros iniciales están bien, como en . ¿Qué sucede si intentas esto en Python?
Qué pasa ?
5. ¿Qué pasa si tienes dos valores sin operador entre ellos?
Ejercicio 1.2. Inicie el intérprete de Python y utilícelo como calculadora.
1. ¿Cuántos segundos hay en 42 minutos 42 segundos?
2. ¿Cuántas millas hay en 10 kilómetros? Pista: hay 1,61 kilómetros en una milla.
3. Si corres una carrera de 10 kilómetros en 42 minutos y 42 segundos, ¿cuál es tu ritmo promedio (tiempo
por milla en minutos y segundos)? ¿Cuál es su velocidad promedio en millas por hora?
Machine Translated by Google
Capitulo 2
Variables, expresiones y
declaraciones
Una de las características más poderosas de un lenguaje de programación es la capacidad de manipular
variables. Una variable es un nombre que hace referencia a un valor.
2.1 Declaraciones de cesión
Una declaración de asignación crea una nueva variable y le da un valor:
Este ejemplo hace tres asignaciones. El primero asigna una cadena a una nueva variable llamada ; el segundo
da el entero a ; el tercero asigna el valor (aproximado) de
π a .
Una forma común de representar variables en papel es escribir el nombre con una flecha que apunta a su
valor. Este tipo de figura se llama diagrama de estado porque muestra en qué estado se encuentra cada una
de las variables (piense en ello como el estado mental de la variable). La figura 2.1 muestra el resultado del
ejemplo anterior.
2.2 Nombres de variables
Los programadores generalmente eligen nombres significativos para sus variables: documentan para qué se
usa la variable.
mensaje 'Y ahora para algo completamente diferente'
norte 17
Pi 3.1415926535897932
Figura 2.1: Diagrama de estado.
Machine Translated by Google
10 Capítulo 2. Variables, expresiones y sentencias
Los nombres de las variables pueden ser tan largos como desee. Pueden contener tanto letras como
números, pero no pueden comenzar con un número. Es legal usar letras mayúsculas, pero es convencional
usar solo minúsculas para nombres de variables.
El carácter de subrayado, , puede aparecer en un nombre. A menudo se usa en nombres con varias palabras,
como o .
Si le da a una variable un nombre ilegal, obtendrá un error de sintaxis:
es ilegal porque comienza con un número. un es ilegal porque contiene
carácter ilegal, . Pero que tiene de malo ?
Resulta que es una de las palabras clave de Python. El intérprete usa palabras clave para reconocer la
estructura del programa y no se pueden usar como nombres de variables.
Python 3 tiene estas palabras clave:
No tienes que memorizar esta lista. En la mayoría de los entornos de desarrollo, las palabras clave se
muestran en un color diferente; si intenta usar uno como nombre de variable, lo sabrá.
2.3 Expresiones y declaraciones
Una expresión es una combinación de valores, variables y operadores. Un valor en sí mismo se considera
una expresión, al igual que una variable, por lo que las siguientes son todas expresiones legales:
Cuando escribe una expresión en el indicador, el intérprete la evalúa , lo que significa que encuentra el valor
de la expresión. En este ejemplo, tiene el valor 17 y tiene el valor 42.
Una declaración es una unidad de código que tiene un efecto, como crear una variable o mostrar un valor.
Machine Translated by Google
2.4. Modo de secuencia de comandos
11
La primera línea es una declaración de asignación que da un valor a . La segunda línea es una declaración
de impresión que muestra el valor de .
Cuando escribe una declaración, el intérprete la ejecuta , lo que significa que hace lo que dice la
declaración. En general, las declaraciones no tienen valores.
2.4 Modo de guión
Hasta ahora hemos ejecutado Python en modo interactivo, lo que significa que interactúas directamente
con el intérprete. El modo interactivo es una buena manera de comenzar, pero si trabaja con más de unas
pocas líneas de código, puede resultar complicado.
La alternativa es guardar el código en un archivo llamado secuencia de comandos y luego ejecutar el intérprete en modo secuencia
de comandos para ejecutar la secuencia de comandos. Por convención, los scripts de Python tienen nombres que terminan con .
Si sabe cómo crear y ejecutar un script en su computadora, está listo para comenzar. De lo contrario,
recomiendo usar PythonAnywhere nuevamente. He publicado instrucciones para ejecutar en modo script
en .
Debido a que Python proporciona ambos modos, puede probar fragmentos de código en modo interactivo
antes de colocarlos en un script. Pero existen diferencias entre el modo interactivo y el modo script que
pueden resultar confusos.
Por ejemplo, si está utilizando Python como calculadora, puede escribir
,
La primera línea asigna un valor a pero no tiene ningún efecto visible. La segunda línea es una expresión,
por lo que el intérprete la evalúa y muestra el resultado. Resulta que un maratón son unos 42 kilómetros.
Pero si escribe el mismo código en un script y lo ejecuta, no obtiene ningún resultado. En modo script, una
expresión, por sí misma, no tiene ningún efecto visible. Python evalúa la expresión, pero no muestra el
resultado. Para mostrar el resultado, necesita una declaración como esta:
Este comportamiento puede ser confuso al principio. Para verificar su comprensión, escriba las siguientes
declaraciones en el intérprete de Python y vea lo que hacen:
Ahora ponga las mismas declaraciones en un script y ejecútelo. ¿Cuál es la salida? Modifique el script
transformando cada expresión en una declaración de impresión y luego ejecútelo nuevamente.
2.5 Orden de las operaciones
Cuando una expresión contiene más de un operador, el orden de evaluación depende del orden de las
operaciones. Para los operadores matemáticos, Python sigue la convención matemática. El acrónimo
PEMDAS es una forma útil de recordar las reglas:
Machine Translated by Google
12 Capítulo 2. Variables, expresiones y sentencias
• Los paréntesis tienen la prioridad más alta y se pueden usar para forzar que una expresión se
evalúe en el orden que desee. Dado que las expresiones entre paréntesis se evalúan primero,
es 4 y es 8. También puede usar paréntesis para hacer que una expresión sea
más fácil de leer, incluso si no cambia el resultado. ,
• La exponenciación tiene la siguiente prioridad más alta, por lo es 9, no 27, y
que es 18, no 36.
• La multiplicación y la división tienen mayor prioridad que la suma y la resta. es 5, no 4, y es 8,
Entonces
no 5.
• Los operadores con la misma precedencia se evalúan de izquierda a derecha (excepto
exponenciación). Entonces, en la expresión, la división ,ocurre primero y el resultado se
multiplica por . Para dividir por 2π, puedes usar paréntesis o escribir
.
No trabajo muy duro para recordar la precedencia de los operadores. Si no puedo decirlo mirando la
expresión, uso paréntesis para que sea obvio.
2.6 Operaciones con cadenas
En general, no puede realizar operaciones matemáticas en cadenas, incluso si las cadenas parecen
números, por lo que las siguientes son ilegales:
Pero hay dos excepciones, y .
El operador realiza la concatenación de cadenas, lo que significa que une las cadenas al vincularlas
de extremo a extremo. Por ejemplo:
El operador también trabaja en cadenas; realiza la repetición. Por ejemplo, si uno de es
. los valores es una cadena, el otro debe ser un número entero.
Este uso de y tiene sentido por analogía con la suma y la multiplicación. Así como equivale a ser
lo mismo que y es. Por o,tro
esperamos ,
lado, hay una forma significativa en la que la concatenación y repetición
de cadenas son diferentes de la suma y multiplicación de enteros. ¿Puedes pensar en una propiedad
que tenga la suma y que no tenga la concatenación de cadenas?
2.7 Comentarios
A medida que los programas se hacen más grandes y complicados, se vuelven más difíciles de leer. Los
lenguajes formales son densos y, a menudo, es difícil mirar un fragmento de código y descubrir qué está
haciendo o por qué.
Machine Translated by Google
2.8. depuración 13
Por esta razón, es una buena idea agregar notas a sus programas para explicar en lenguaje natural lo que está
haciendo el programa. Estas notas se denominan comentarios y comienzan con el símbolo:
En este caso, el comentario aparece solo en una línea. También puede poner comentarios al final de una línea:
Se ignora todo, desde el hasta el final de la línea; no tiene ningún efecto en la ejecución del programa.
Los comentarios son más útiles cuando documentan características no obvias del código. Es razonable suponer
que el lector puede averiguar qué hace el código; es más útil explicar por qué.
Este comentario es redundante con el código e inútil:
Este comentario contiene información útil que no está en el código:
Los buenos nombres de variables pueden reducir la necesidad de comentarios, pero los nombres largos pueden dificultar la
lectura de expresiones complejas, por lo que existe una compensación.
2.8 Depuración
Pueden ocurrir tres tipos de errores en un programa: errores de sintaxis, errores de tiempo de ejecución y errores
semánticos. Es útil distinguirlos para localizarlos más rápidamente.
Error de sintaxis: “Sintaxis” se refiere a la estructura de un programa y las reglas sobre esa estructura. Por ejemplo,
los paréntesis deben venir en pares coincidentes, por lo que es legal, pero es un error de sintaxis.
Si hay un error de sintaxis en cualquier parte de su programa, Python muestra un mensaje de error y se
cierra, y no podrá ejecutar el programa. Durante las primeras semanas de su carrera como programador,
puede pasar mucho tiempo rastreando errores de sintaxis. A medida que gane experiencia, cometerá menos
errores y los encontrará más rápido.
Error de tiempo de ejecución: el segundo tipo de error es un error de tiempo de ejecución, llamado así porque el
error no aparece hasta que el programa ha comenzado a ejecutarse. Estos errores también se denominan
excepciones porque suelen indicar que ha ocurrido algo excepcional (y malo).
Los errores de tiempo de ejecución son raros en los programas simples que verá en los primeros capítulos,
por lo que puede pasar un tiempo antes de que encuentre uno.
Error semántico: el tercer tipo de error es el “semántico”, que significa relacionado con el significado.
Si hay un error semántico en su programa, se ejecutará sin generar mensajes de error, pero no hará lo
correcto. Hará otra cosa. Específicamente, hará lo que le dijiste que hiciera.
Identificar errores semánticos puede ser complicado porque requiere que trabajes hacia atrás mirando la
salida del programa y tratando de averiguar qué está haciendo.
Machine Translated by Google
14 Capítulo 2. Variables, expresiones y sentencias
2.9 Glosario
variable: Un nombre que se refiere a un valor.
asignación: Una declaración que asigna un valor a una variable.
diagrama de estado: Una representación gráfica de un conjunto de variables y los valores a los que se refieren.
palabra clave: una palabra reservada que se utiliza para analizar un programa; no puedes usar palabras clave como
, ,y como nombres de variables.
operando: Uno de los valores sobre los que opera un operador.
expresión: Una combinación de variables, operadores y valores que representa una sola re
resultado
evaluar: Simplificar una expresión realizando las operaciones para producir una sola
valor.
declaración: Una sección de código que representa un comando o acción. Hasta ahora, las sentencias
que hemos visto son asignaciones y sentencias de impresión.
ejecutar: para ejecutar una declaración y hacer lo que dice.
modo interactivo: una forma de usar el intérprete de Python escribiendo código en el aviso.
modo script: una forma de usar el intérprete de Python para leer el código de un script y ejecutarlo.
script: Un programa almacenado en un archivo.
orden de operaciones: Reglas que rigen el orden en que se evalúan las expresiones que involucran
múltiples operadores y operandos.
concatenar: unir dos operandos de extremo a extremo.
comentario: Información en un programa que está destinada a otros programadores (o cualquiera que
lea el código fuente) y no tiene efecto en la ejecución del programa.
error de sintaxis: Un error en un programa que hace que sea imposible de analizar (y por lo tanto im
posible de interpretar).
excepción: un error que se detecta mientras se ejecuta el programa.
semántica: El significado de un programa.
error semántico: un error en un programa que hace que haga algo diferente de lo que pretendía el
programador.
2.10 Ejercicios
Ejercicio 2.1. Repitiendo mi consejo del capítulo anterior, cada vez que aprenda una nueva función, debe
probarla en modo interactivo y cometer errores a propósito para ver qué sale mal.
• Hemos visto que es legal. Qué pasa ?
Machine Translated by Google
2.10. Ejercicios 15
• Qué tal si ?
• En algunos idiomas, cada declaración termina con un punto y coma, . ¿Qué sucede si coloca un punto y coma
al final de una declaración de Python?
• ¿Qué sucede si pone un punto al final de una declaración?
• En notación matemática puedes multiplicar x e y así: xy. ¿Qué pasa si intentas eso en
¿Pitón?
Ejercicio 2.2. Practique usando el intérprete de Python como calculadora:
4 3 πr
1. El volumen de una esfera de radio r es 3 . ¿Cuál es el volumen de una esfera de radio 5?
2. Supón que el precio de tapa de un libro es $24.95, pero las librerías obtienen un 40% de descuento. El envío
cuesta $3 por la primera copia y 75 centavos por cada copia adicional. ¿Cuál es el costo total al por mayor
de 60 copias?
3. Si salgo de mi casa a las 6:52 a. m. y corro 1 milla a un ritmo suave (8:15 por milla), luego 3 millas a un ritmo
lento (7:12 por milla) y 1 milla a un ritmo suave de nuevo, ¿a qué hora? llego a casa a desayunar?
Machine Translated by Google
dieciséis
Capítulo 2. Variables, expresiones y sentencias
Machine Translated by Google
Capítulo 3
Funciones
En el contexto de la programación, una función es una secuencia con nombre de declaraciones
que realiza un cálculo. Cuando define una función, especifica el nombre y la secuencia de
declaraciones. Más tarde, puede "llamar" a la función por su nombre.
3.1 Llamadas de función
Ya hemos visto un ejemplo de una llamada de función:
El nombre de la función es . La expresión entre paréntesis se llama argumento de la función. El
resultado, para esta función, es el tipo del argumento.
Es común decir que una función “toma” un argumento y “devuelve” un resultado. El resultado
también se denomina valor de retorno.
Python proporciona funciones que convierten valores de un tipo a otro. La función toma cualquier
valor y lo convierte en un número entero, si puede, o se queja de lo contrario:
puede convertir valores de coma flotante en enteros, pero no redondea; corta la parte
fraccionaria:
convierte enteros y cadenas a números de punto flotante:
Machine Translated by Google
18 Capítulo 3. Funciones
Finalmente, convierte su argumento en una cadena:
3.2 Funciones matemáticas
Python tiene un módulo matemático que proporciona la mayoría de las funciones matemáticas familiares. Un
módulo es un archivo que contiene una colección de funciones relacionadas.
Antes de que podamos usar las funciones en un módulo, debemos importarlo con un estado de importación
mento:
Esta declaración crea un objeto de módulo llamado matemáticas. Si muestra el objeto del módulo, obtendrá
información al respecto:
El objeto del módulo contiene las funciones y variables definidas en el módulo. Para acceder a una de las
funciones, debe especificar el nombre del módulo y el nombre de la función, separados por un punto (también
conocido como punto). Este formato se llama notación de puntos.
El primer ejemplo usa that para calcular una relación señalruido en decibelios (suponiendo que
y which calcula la base de estén definidos). El módulo de matemáticas también proporciona ,
logaritmos.
El segundo ejemplo encuentra el seno de . El nombre de la variable es una pista de que las
otras funciones trigonométricas (, , etc.) toman argumentos en radianes. A
convertir de grados a radianes, dividir por 180 y multiplicar por π:
La expresión obtiene la variable del módulo matemático. Su valor es una aproximación de coma flotante de
π, con una precisión de unos 15 dígitos.
Si sabes trigonometría, puedes comprobar el resultado anterior comparándolo con la raíz cuadrada de dos,
dividida por dos:
Machine Translated by Google
3.3. Composición 19
3.3 Composición
Hasta ahora, hemos visto los elementos de un programa (variables, expresiones y sentencias) de forma aislada, sin
hablar de cómo combinarlos.
Una de las características más útiles de los lenguajes de programación es su capacidad para tomar pequeños
bloques de construcción y componerlos . Por ejemplo, el argumento de una función puede ser cualquier tipo de
expresión, incluidos los operadores aritméticos:
E incluso llamadas a funciones:
Casi en cualquier lugar donde pueda poner un valor, puede poner una expresión arbitraria, con una excepción: el
lado izquierdo de una declaración de asignación tiene que ser un nombre de variable. Cualquier otra expresión en
el lado izquierdo es un error de sintaxis (veremos excepciones a esta regla más adelante).
3.4 Agregar nuevas funciones
Hasta ahora, solo hemos estado usando las funciones que vienen con Python, pero también es posible agregar
nuevas funciones. Una definición de función especifica el nombre de una nueva función y la secuencia de
instrucciones que se ejecutan cuando se llama a la función.
Aquí hay un ejemplo:
es una palabra clave que indica que se trata de una definición de función. El nombre de la función es . Las
reglas para los nombres de funciones son las mismas que para los nombres de variables: letras, números y guiones
bajos son válidos, pero el primer carácter no puede ser un número. No puede usar una palabra clave como nombre
de una función y debe evitar tener una variable y una función con el mismo nombre.
Los paréntesis vacíos después del nombre indican que esta función no acepta ningún argumento.
mentos.
La primera línea de la definición de la función se llama encabezado; el resto se llama el cuerpo. El encabezado debe
terminar con dos puntos y el cuerpo debe estar sangrado. Por convención, la sangría es siempre de cuatro espacios.
El cuerpo puede contener cualquier número de sentencias.
Las cadenas en las sentencias de impresión están encerradas entre comillas dobles. Las comillas simples y las
comillas dobles hacen lo mismo; la mayoría de la gente usa comillas simples, excepto en casos como este, donde
aparece una comilla simple (que también es un apóstrofo) en la cadena.
Todas las comillas (simples y dobles) deben ser "comillas rectas", generalmente ubicadas junto a Enter en el teclado.
Las “comillas rizadas”, como las de esta oración, no son legales en Python.
Si escribe una definición de función en modo interactivo, el intérprete imprime puntos () para informarle que la
definición no está completa:
Machine Translated by Google
20 Capítulo 3. Funciones
Para finalizar la función, debe ingresar una línea vacía.
La definición de una función crea un objeto de función, que tiene tipo :
La sintaxis para llamar a la nueva función es la misma que para las funciones integradas:
Una vez que haya definido una función, puede usarla dentro de otra función. Por ejemplo, para repetir el
estribillo anterior, podríamos escribir una función llamada :
y luego llamar :
Pero así no es realmente como va la canción.
3.5 Definiciones y usos
Reuniendo los fragmentos de código de la sección anterior, todo el programa se ve así:
Este programa contiene dos definiciones de función: y las definiciones de ción . Función
se ejecutan como otras declaraciones, pero el efecto es crear objetos de función. Las instrucciones dentro
de la función no se ejecutan hasta que se llama a la función y la definición de la función no genera ningún
resultado.
Machine Translated by Google
3.6. Flujo de ejecución 21
Como es de esperar, debe crear una función antes de poder ejecutarla. En otras palabras, la definición de
la función debe ejecutarse antes de que se llame a la función.
Como ejercicio, mueva la última línea de este programa hacia arriba, para que la llamada a la función
aparezca antes de las definiciones. Ejecute el programa y vea qué mensaje de error recibe.
Ahora mueva la llamada de función al final y mueva la definición de después de la
definición de . ¿Qué sucede cuando ejecutas este programa?
3.6 Flujo de ejecución
Para asegurarse de que una función se define antes de su primer uso, debe conocer el orden en que se
ejecutan las sentencias, lo que se denomina flujo de ejecución.
La ejecución siempre comienza en la primera instrucción del programa. Las declaraciones se ejecutan una
a la vez, en orden de arriba a abajo.
Las definiciones de función no alteran el flujo de ejecución del programa, pero recuerde que las
declaraciones dentro de la función no se ejecutan hasta que se llama a la función.
Una llamada de función es como un desvío en el flujo de ejecución. En lugar de ir a la siguiente declaración,
el flujo salta al cuerpo de la función, ejecuta las declaraciones allí y luego regresa para continuar donde lo
dejó.
Eso suena bastante simple, hasta que recuerdas que una función puede llamar a otra. Mientras se
encuentra en medio de una función, es posible que el programa deba ejecutar las declaraciones en otra
función. Luego, mientras ejecuta esa nueva función, ¡el programa podría tener que ejecutar otra función!
Afortunadamente, Python es bueno para hacer un seguimiento de dónde está, por lo que cada vez que se
completa una función, el programa continúa donde se quedó en la función que la llamó. Cuando llega al
final del programa, termina.
En resumen, cuando lees un programa, no siempre quieres leer de arriba a abajo.
A veces tiene más sentido si sigues el flujo de ejecución.
3.7 Parámetros y argumentos
Algunas de las funciones que hemos visto requieren argumentos. Por ejemplo, cuando llamas pasas un
número como argumento. Algunas funciones toman más de uno o toman dos, la base y el
cemento: exponente.
Dentro de la función, los argumentos se asignan a variables llamadas parámetros. Aquí hay una definición
para una función que toma un argumento:
Esta función asigna el argumento a un parámetro llamado llamado, . Cuando la función es
imprime el valor del parámetro (sea cual sea) dos veces.
Esta función funciona con cualquier valor que se pueda imprimir.
Machine Translated by Google
22 Capítulo 3. Funciones
Las mismas reglas de composición que se aplican a las funciones integradas también se aplican a las funciones
definidas por el programador, por lo que podemos usar cualquier tipo de expresión como argumento para :
El argumento se evalúa antes de llamar a la función, por lo que en los ejemplos las expresiones
y solo se evalúan una vez.
También puede usar una variable como argumento:
El nombre de la variable que pasamos como argumento () no tiene nada que ver con el nombre del
parámetro (). No importa cuál fue el valor llamado en casa (en la persona que llama); aquí en llamamos
a todos , .
3.8 Las variables y los parámetros son locales
Cuando crea una variable dentro de una función, es local, lo que significa que solo existe dentro de la
función. Por ejemplo:
Esta función toma dos argumentos, los concatena e imprime el resultado dos veces. Aquí hay un
ejemplo que lo usa:
3.9. diagramas de pila 23
línea 1 'Bing tiddle' 'tiddle
__principal__
línea 2 bang'.
parte 1 'Bing tiddle'
gato tiddle tiddle bang'.
Figura 3.1: Diagrama de pila.
Los parámetros también son locales. Por ejemplo, fuera , no hay tal cosa como
.
3.9 Diagramas de pila
Para realizar un seguimiento de qué variables se pueden usar en qué lugar, a veces es útil dibujar un diagrama
de pila. Al igual que los diagramas de estado, los diagramas de pila muestran el valor de cada variable, pero
también muestran la función a la que pertenece cada variable.
Cada función está representada por un marco. Un marco es un cuadro con el nombre de una función
al lado y los parámetros y variables de la función dentro. El diagrama de pila para el ejemplo anterior
se muestra en la Figura 3.1.
Los marcos se organizan en una pila que indica qué función llamó a cuál, y así sucesivamente.
,
En este ejemplo, fue llamado por y fue llamado por, que es un nombre
especial para el marco
,
superior. Cuando crea una variable fuera de cualquier función, pertenece a
.
Cada parámetro hace referencia al mismo valor que su argumento correspondiente. tiene el
Entonces, el mismo valor
,que tiene el mismo valor que y , mismo valor que
.
Si ocurre un error durante una llamada de función, Python imprime el nombre de la función, el
nombre de la función que la llamó y el nombre de la función que la llamó, todo el camino de
vuelta a .
Por ejemplo, si intenta acceder desde dentro , obtienes un :
Machine Translated by Google
24 Capítulo 3. Funciones
Esta lista de funciones se denomina rastreo. Le dice en qué archivo de programa ocurrió el error, en qué línea y
qué funciones se estaban ejecutando en ese momento. También muestra la línea de código que provocó el error.
El orden de las funciones en el rastreo es el mismo que el orden de los marcos en el diagrama de pila. La función
que se está ejecutando actualmente se encuentra en la parte inferior.
3.10 Funciones fructíferas y funciones nulas
Algunas de las funciones que hemos utilizado, como las funciones matemáticas, devuelven resultados; a falta de
mejor nombre, las llamo funciones fecundas. Otras funciones, como , realizan una acción pero no devuelven un
valor. Se llaman funciones nulas.
Cuando llamas a una función fructífera, casi siempre quieres hacer algo con el resultado; por ejemplo, puede
asignarlo a una variable o usarlo como parte de una expresión:
Cuando llama a una función en modo interactivo, Python muestra el resultado:
Pero en un script, si llama a una función fructífera por sí misma, ¡el valor de retorno se pierde para siempre!
Este script calcula la raíz cuadrada de 5, pero como no almacena ni muestra el resultado, no es muy útil.
Las funciones de anulación pueden mostrar algo en la pantalla o tener algún otro efecto, pero no tienen un valor de
retorno. Si asigna el resultado a una variable, obtiene un valor especial llamado
.
Las funciones que hemos escrito hasta ahora son todas nulas. Comenzaremos a escribir funciones fructíferas en
unos pocos capítulos.
3.11 ¿Por qué funciones?
Puede que no esté claro por qué vale la pena dividir un programa en funciones. Hay varias razones:
Machine Translated by Google
3.12. depuración 25
• La creación de una nueva función le brinda la oportunidad de nombrar un grupo de declaraciones, lo
que hace que su programa sea más fácil de leer y depurar.
• Las funciones pueden hacer que un programa sea más pequeño al eliminar el código repetitivo.
Posteriormente, si realiza un cambio, solo tiene que hacerlo en un lugar.
• Dividir un programa largo en funciones le permite depurar las partes una a la vez
y luego ensamblarlos en un todo funcional.
• Las funciones bien diseñadas suelen ser útiles para muchos programas. Una vez que escribe y depura
uno, puede reutilizarlo.
3.12 Depuración
Una de las habilidades más importantes que adquirirá es la depuración. Aunque puede ser frustrante, la
depuración es una de las partes intelectualmente más ricas, desafiantes e interesantes de la programación.
En cierto modo, la depuración es como un trabajo de detective. Te enfrentas a pistas y tienes que inferir los
procesos y eventos que llevaron a los resultados que ves.
La depuración también es como una ciencia experimental. Una vez que tenga una idea de lo que va mal,
modifique su programa y vuelva a intentarlo. Si su hipótesis fue correcta, puede predecir el resultado de la
modificación y dar un paso más hacia un programa que funcione. Si su hipótesis fue incorrecta, debe pensar
en una nueva. Como señaló Sherlock Holmes: “Cuando has eliminado lo imposible, lo que quede, por
improbable que sea, debe ser la verdad”. (A. Conan Doyle, El signo de los cuatro)
Para algunas personas, la programación y la depuración son lo mismo. Es decir, la programación es el
proceso de depuración gradual de un programa hasta que hace lo que quieres. La idea es que debe
comenzar con un programa que funcione y hacer pequeñas modificaciones, depurándolas sobre la marcha.
Por ejemplo, Linux es un sistema operativo que contiene millones de líneas de código, pero comenzó como
un programa simple que Linus Torvalds usó para explorar el chip Intel 80386. Según Larry Greenfield, “Uno
de los primeros proyectos de Linus fue un programa que cambiaría entre la impresión AAAA y BBBB. Esto
luego evolucionó a Linux”. (La Guía del Usuario de Linux Versión Beta 1).
3.13 Glosario
función: Una secuencia con nombre de sentencias que realiza alguna operación útil. Las funciones pueden
o no tomar argumentos y pueden o no producir un resultado.
definición de función: una declaración que crea una nueva función, especificando su nombre, parámetros y
las declaraciones que contiene.
objeto de función: Un valor creado por una definición de función. El nombre de la función es una variable
que hace referencia a un objeto de función.
encabezado: La primera línea de una definición de función.
Machine Translated by Google
26 Capítulo 3. Funciones
cuerpo: La secuencia de sentencias dentro de una definición de función.
parámetro: Un nombre usado dentro de una función para referirse al valor pasado como argumento.
Llamada de función: una instrucción que ejecuta una función. Consiste en el nombre de la función seguido de una
lista de argumentos entre paréntesis.
argumento: un valor proporcionado a una función cuando se llama a la función. Este valor es como
firmado al parámetro correspondiente en la función.
variable local: Una variable definida dentro de una función. Una variable local sólo se puede utilizar
dentro de su función.
valor de retorno: El resultado de una función. Si se utiliza una llamada de función como expresión, el valor devuelto
es el valor de la expresión.
función fructífera: una función que devuelve un valor.
función nula: una función que siempre devuelve .
: Un valor especial devuelto por funciones nulas.
módulo: Un archivo que contiene una colección de funciones relacionadas y otras definiciones.
declaración de importación: una declaración que lee un archivo de módulo y crea un objeto de módulo.
objeto de módulo: Un valor creado por un definido declaración que proporciona acceso a los valores
en un módulo.
notación de puntos: la sintaxis para llamar a una función en otro módulo especificando el nombre del módulo
seguido de un punto (punto) y el nombre de la función.
composición: Usar una expresión como parte de una expresión más grande, o una declaración como parte de una
declaración más grande.
Flujo de ejecución: las instrucciones de orden se ejecutan.
diagrama de pila: una representación gráfica de una pila de funciones, sus variables y la
valores a los que se refieren.
marco: un cuadro en un diagrama de pila que representa una llamada de función. Contiene el local vari
funciones y parámetros de la función.
traceback: Una lista de las funciones que se están ejecutando, impresa cuando ocurre una excepción.
3.14 Ejercicios
Ejercicio 3.1. Escriba una función named que tome una cadena named como parámetro e imprima la cadena con
suficientes espacios iniciales para que la última letra de la cadena esté en la columna 70 de la pantalla.
Sugerencia: utilice la concatenación y repetición de cadenas. Además, Python proporciona una función integrada llamada
que devuelve la longitud de una cadena, por lo que el valor de es 5.
Machine Translated by Google
3.14. Ejercicios 27
Ejercicio 3.2. Un objeto de función es un valor que puede asignar a una variable o pasar como argumento. Por ejemplo, es una función
que toma un objeto de función como argumento y lo llama dos veces:
dos veces.
He aquí un ejemplo que utiliza para llamar a una función llamada
1. Escriba este ejemplo en un script y pruébelo.
2. Modifique para que tome dos argumentos, un objeto de función y un valor, y llame a la función dos veces, pasando el valor como
argumento.
3. Copie la definición de desde antes en este capítulo a su script.
5. Defina una nueva función llamada que tome un objeto de función y un valor y llame a la función cuatro veces, pasando el valor
como parámetro. Debe haber solo dos declaraciones en el cuerpo de esta función, no cuatro.
Solución: .
Ejercicio 3.3. Nota: este ejercicio debe realizarse utilizando solo las declaraciones y otras características que hemos aprendido hasta
ahora.
1. Escribe una función que dibuje una cuadrícula como la siguiente:
Sugerencia: para imprimir más de un valor en una línea, puede imprimir una secuencia de valores separados por comas:
De forma predeterminada, avanza a la siguiente línea, pero puede anular ese comportamiento y poner un espacio al final, así:
Machine Translated by Google
28 Capítulo 3. Funciones
La salida de estas declaraciones es una en la misma línea. El resultado de la próxima impresión
declaración que comenzaría en la siguiente línea.
2. Escribe una función que dibuje una cuadrícula similar con cuatro filas y cuatro columnas.
Solución: . Crédito: este ejercicio se basa en un ejercicio en Oualline, Practical C Programming, Third Edition, O'Reilly
Media, 1997.
Machine Translated by Google
Capítulo 4
Estudio de caso: diseño de interfaz
Este capítulo presenta un caso de estudio que demuestra un proceso para diseñar funciones que
funcionan juntas.
Presenta el módulo, que le permite crear imágenes utilizando gráficos de tortugas.
El módulo está incluido en la mayoría de las instalaciones de Python, pero si está ejecutando Python
usando PythonAnywhere, no podrá ejecutar los ejemplos de tortugas (al menos no podía cuando
escribí esto).
Si ya instaló Python en su computadora, debería poder ejecutar los ejemplos. De lo contrario, ahora
es un buen momento para instalar. He publicado instrucciones en
.
Los ejemplos de código de este capítulo están disponibles en
.
4.1 El módulo tortuga
Para comprobar si tiene la módulo, abra el intérprete de Python y escriba
Cuando ejecuta este código, debería crear una nueva ventana con una pequeña flecha que representa
la tortuga. Cerrar la ventana.
Crear un archivo llamado y escriba el siguiente código:
El módulo (con una 't' minúscula) proporciona una función llamada (con una 'T' mayúscula) que crea
un objeto Turtle, que asignamos a una variable llamada . La impresión muestra algo como:
Machine Translated by Google
30 Capítulo 4. Caso de estudio: diseño de interfaz
le dice a la ventana que espere a que el usuario haga algo, aunque en este caso el usuario no tiene
mucho que hacer excepto cerrar la ventana.
Una vez que creas una tortuga, puedes llamar a un método para moverla por la ventana. Un método es similar a
una función, pero usa una sintaxis ligeramente diferente. Por ejemplo, para mover la tortuga hacia adelante:
El método, está asociado con el objeto de tortuga que estamos llamando como . Llamar a un método es
hacer una solicitud: estás preguntando avanzar.
El argumento de es una distancia en píxeles, por lo que el tamaño real depende de su pantalla.
Otros métodos a los que puede llamar a una tortuga son moverse hacia atrás, girar a la izquierda y girar a la
derecha. El argumento a favor de y es un ángulo en grados.
Además, cada tortuga sostiene un bolígrafo, que está hacia abajo o hacia arriba; si el bolígrafo está abajo, la
Tortuga deja un rastro cuando se mueve. Los métodos y representan "pluma arriba" y "pluma abajo".
Para dibujar un ángulo recto, agregue estas líneas al programa (después de crear y antes de llamar
):
Cuando ejecute este programa, debería ver moverse hacia el este y luego hacia el norte, dejando atrás dos
segmentos de línea.
Ahora modifica el programa para dibujar un cuadrado. ¡No continúes hasta que lo tengas funcionando!
4.2 Repetición simple
Lo más probable es que hayas escrito algo como esto:
Podemos hacer lo mismo de forma más concisa con a y declaración. Agrega este ejemplo a
ejecutarlo de nuevo:
Debería ver algo como esto:
Machine Translated by Google
4.3. Ejercicios 31
Este es el uso más simple de la declaración; veremos más adelante. Pero eso debería ser suficiente para
permitirle reescribir su programa de dibujo de cuadrados. No continúes hasta que lo hagas.
Aquí hay un enunciado que dibuja un cuadrado:
La sintaxis de a declaración es similar a una definición de función. Tiene un encabezado que termina
con dos puntos y cuerpo sangrado. El cuerpo puede contener cualquier número de sentencias.
Una declaración también se denomina bucle porque el flujo de ejecución se ejecuta a través del cuerpo
y luego vuelve a la parte superior. En este caso, se ejecuta el cuerpo cuatro veces.
Esta versión es en realidad un poco diferente del código de dibujo de cuadrados anterior porque hace
otro giro después de dibujar el último lado del cuadrado. El turno adicional toma más tiempo, pero
simplifica el código si hacemos lo mismo cada vez que se ejecuta el ciclo. Esta versión también tiene
el efecto de dejar a la tortuga en la posición inicial, mirando en la dirección inicial.
4.3 Ejercicios
La siguiente es una serie de ejercicios usando el módulo. Están destinados a ser divertidos, pero
también tienen un punto. Mientras trabaja en ellos, piense cuál es el punto.
Las siguientes secciones tienen soluciones para los ejercicios, así que no busques hasta que hayas terminado (o al
menos lo hayas intentado).
1. Escriba una función llamada que tome un parámetro llamado debería usar , que es una tortuga. Él
la tortuga para dibujar un cuadrado.
Escriba una llamada de función que pase como argumento para programar , y luego ejecuta el
nuevamente.
2. Agregue otro parámetro, denominado . Modifique el , cauerpo para que la longitud de los lados sea y luego
modifique la llamada
, a la función para proporcionar un segundo argumento. Vuelva a ejecutar el programa.
Pruebe su programa con un rango de valores para .
3. Haz una copia de y cambie el nombre a . Agregue otro parámetro llamado y modifique
el cuerpo para que dibuje un polígono regular de n lados. Pista: Los ángulos exteriores de un
polígono regular de n lados son 360/n grados.
4. Escribe una función llamada que toma como parámetros una tortuga, , y un radio, y con
que dibuja un círculo aproximado llamando una longitud y
número de lados. Pruebe su función con un rango de valores de .
Pista: calcula la circunferencia del círculo y asegúrate de que
.
5. Haz una versión más general de llamado que que toma un parámetro adicional está
, determina qué fracción de un círculo dibujar. debe en unidades de grados,
así que cuando , dibujar un círculo completo.
Machine Translated by Google
32 Capítulo 4. Caso de estudio: diseño de interfaz
4.4 Encapsulación
El primer ejercicio le pide que coloque su código de dibujo cuadrado en una definición de función y luego
llame a la función, pasando la tortuga como parámetro. Aquí hay una solución:
Las declaraciones más internas, y están sangradas dos veces para mostrar que están dentro de la está al
bucle, que está dentro de la definición de la función. La siguiente línea, , ras con
el margen izquierdo, que indica el final de ambos bucle y la definición de la función.
Envolver una pieza de código en una función se llama encapsulación. Uno de los beneficios de la
encapsulación es que le asigna un nombre al código, que sirve como una especie de documentación. Otra
ventaja es que si reutiliza el código, ¡es más conciso llamar a una función dos veces que copiar y pegar el
cuerpo!
4.5 Generalización
El siguiente paso es agregar un parámetro a . Aquí hay una solución:
Agregar un parámetro a una función se llama generalización porque hace que la función sea más general:
en la versión anterior, el cuadrado siempre tiene el mismo tamaño; en esta versión puede ser de cualquier
tamaño.
El siguiente paso es también una generalización. En lugar de dibujar cuadrados, dibuja regularmente
polígonos con cualquier número de lados. Aquí hay una solución:
Machine Translated by Google
4.6. Diseño de interfaz 33
Este ejemplo dibuja un polígono de 7 lados con una longitud de lado de 70.
Si está utilizando Python 2, el valor de la solución podría estar apagado debido a la división de enteros.
simple es calcular el número de A . porque el numerador es flotante
puntos, el resultado es un punto flotante.
Cuando una función tiene más de unos pocos argumentos numéricos, es fácil olvidar cuáles son o en qué orden
deberían estar. En ese caso, a menudo es una buena idea incluir los nombres de los parámetros en la lista de
argumentos:
Estos se denominan argumentos de palabras clave porque incluyen los nombres de los parámetros como
"palabras clave" (que no deben confundirse con las palabras clave de Python como y ).
Esta sintaxis hace que el programa sea más legible. También es un recordatorio sobre cómo funcionan los
argumentos y parámetros: cuando llama a una función, los argumentos se asignan al parámetro
éteres.
4.6 Diseño de la interfaz
El siguiente paso es escribir , que toma un radio, , como parámetro. Aquí hay un simple para dibujar
una solución que use un polígono de 50 lados:
La primera línea calcula la circunferencia de un círculo con radio usando la fórmula 2πr.
Ya que usamos tenemos que , importar . Por convención, normalmente al principio las declaraciones son
del guión.
es el número de segmentos de línea en nuestra aproximación de un círculo, por lo es la longitud
que de cada segmento. Por dibuja un polígono de 50 lados que se aproxima a un círculo con
lo tanto, radio .
Una limitación de esta solución es que es una constante, lo que significa que para círculos muy grandes, los
segmentos de línea son demasiado largos, y para círculos pequeños, perdemos tiempo dibujando segmentos
muy pequeños. Una solución sería generalizar la función tomando como parámetro.
Esto le daría al usuario (quienquiera que llame) más control, pero la interfaz sería menos limpia.
La interfaz de una función es un resumen de cómo se utiliza: ¿cuáles son los parámetros? ¿Qué hace la función?
¿Y cuál es el valor de retorno? Una interfaz es "limpia" si permite que la persona que llama haga lo que quiera
sin tener que lidiar con detalles innecesarios.
En este ejemplo, pertenece a la interfaz porque especifica el círculo que se dibujará. es menos apropiado porque
se refiere a los detalles de cómo se debe representar el círculo.
En lugar de saturar la interfaz, es mejor elegir un valor apropiado de dependiendo de
:
Machine Translated by Google
34 Capítulo 4. Caso de estudio: diseño de interfaz
Ahora, el número de segmentos es un número entero cercano, por lo que la longitud de cada ,segmento es de
aproximadamente 3, que es lo suficientemente pequeño para que los círculos se vean bien, pero lo suficientemente grande
para ser eficiente y aceptable para círculos de cualquier tamaño.
Sumar 3 a garantiza que el polígono tiene al menos 3 lados.
4.7 Refactorización
Cuando escribí , pude reutilizar porque un polígono de muchos lados es un bien
aproximación de un círculo. Pero no es tan cooperativo; no podemos usar o a
dibuja un arco.
La segunda mitad de esta función parece , pero no podemos reutilizar sin tomar un
cambiar la interfaz. Podríamos generalizar pero ángulo como tercer argumento, ¡ya no
luego la sería un nombre apropiado! En cambio, llamemos al más
función general :
Ahora podemos reescribir y para usar :
Finalmente, podemos reescribir usar :
Machine Translated by Google
4.8. un plan de desarrollo 35
Este proceso, reorganizar un programa para mejorar las interfaces y facilitar la reutilización del código, se denomina
refactorización. En este caso, nos dimos cuenta de que había un código similar y lo "factorizamos" en ,
.
Si hubiéramos planeado con anticipación, podríamos haber escrito primero y evitado la refactorización, pero a
menudo no se sabe lo suficiente al comienzo de un proyecto para diseñar todas las interfaces.
Una vez que empiezas a codificar, entiendes mejor el problema. A veces, la refactorización es una señal de que
has aprendido algo.
4.8 Un plan de desarrollo
Un plan de desarrollo es un proceso para escribir programas. El proceso que utilizamos en este estudio de caso
es "encapsulación y generalización". Los pasos de este proceso son:
1. Comience escribiendo un pequeño programa sin definiciones de funciones.
2. Una vez que tenga el programa funcionando, identifique una parte coherente del mismo, encapsule la parte
en una función y asígnele un nombre.
3. Generalice la función agregando los parámetros apropiados.
4. Repita los pasos 1 a 3 hasta que tenga un conjunto de funciones operativas. Copiar y pegar trabajando
código para evitar volver a escribir (y volver a depurar).
5. Busque oportunidades para mejorar el programa refactorizando. Por ejemplo, si tiene un código similar en
varios lugares, considere factorizarlo en una función general apropiada.
Este proceso tiene algunos inconvenientes, veremos alternativas más adelante, pero puede ser útil si no sabe de
antemano cómo dividir el programa en funciones. Este enfoque le permite diseñar sobre la marcha.
4.9 cadena de documentación
Una cadena de documentos es una cadena al comienzo de una función que explica la interfaz ("doc" es la
abreviatura de "documentación"). Aquí hay un ejemplo:
Por convención, todas las cadenas de documentos son cadenas entre comillas triples, también conocidas como cadenas
de varias líneas porque las comillas triples permiten que la cadena abarque más de una línea.
Machine Translated by Google
36 Capítulo 4. Caso de estudio: diseño de interfaz
Es breve, pero contiene la información esencial que alguien necesitaría para usar esta función. Explica de manera
concisa lo que hace la función (sin entrar en los detalles de cómo lo hace). Explica qué efecto tiene cada parámetro
en el comportamiento de la función y de qué tipo debe ser cada parámetro (si no es obvio).
Escribir este tipo de documentación es una parte importante del diseño de la interfaz. Una interfaz bien diseñada
debe ser sencilla de explicar; si te cuesta explicar alguna de tus funciones, quizás la interfaz se podría mejorar.
4.10 Depuración
Una interfaz es como un contrato entre una función y una persona que llama. La persona que llama acepta
proporcionar ciertos parámetros y la función acepta realizar cierto trabajo.
Por ejemplo, requiere cuatro argumentos: tiene que ser una tortuga; tiene que ser un número entero; debe ser un
número positivo; y tiene que ser un número, que se entiende en grados.
Estos requisitos se denominan condiciones previas porque se supone que se cumplen antes de que la función
comience a ejecutarse. Por el contrario, las condiciones al final de la función son condiciones posteriores. Las
condiciones posteriores incluyen el efecto previsto de la función (como dibujar segmentos de línea) y cualquier
efecto secundario (como mover la tortuga o realizar otros cambios).
Las condiciones previas son responsabilidad de la persona que llama. Si la persona que llama viola una condición
previa (¡debidamente documentada!) y la función no funciona correctamente, el error está en la persona que llama,
no en la función.
Si se cumplen las condiciones previas y no las poscondiciones, el error está en la función.
Si sus condiciones previas y posteriores son claras, pueden ayudar con la depuración.
4.11 Glosario
método: una función que está asociada con un objeto y se llama usando la notación de puntos.
loop: Una parte de un programa que puede ejecutarse repetidamente.
encapsulación: El proceso de transformar una secuencia de sentencias en una función defi
nición
generalización: El proceso de reemplazar algo innecesariamente específico (como un número) con algo
apropiadamente general (como una variable o parámetro).
argumento de palabra clave: un argumento que incluye el nombre del parámetro como una "clave
palabra".
interfaz: una descripción de cómo usar una función, incluido el nombre y las descripciones de los argumentos y el
valor de retorno.
refactorización: El proceso de modificar un programa de trabajo para mejorar las interfaces de función y otras
cualidades del código.
plan de desarrollo: Un proceso para escribir programas.
Machine Translated by Google
4.12. Ejercicios 37
Figura 4.1: Flores de tortuga.
Figura 4.2: Pasteles de tortuga.
docstring: una cadena que aparece en la parte superior de una definición de función para documentar la función
interfaz de ción.
condición previa: un requisito que debe cumplir la persona que llama antes de que comience una función.
poscondición: Un requisito que debe ser satisfecho por la función antes de que finalice.
4.12 Ejercicios
Ejercicio 4.1. Descargue el código de este capítulo desde
.
1. Dibuje un diagrama de pila que muestre el estado del programa mientras se ejecuta
. Puedes hacer la aritmética a mano o sumar declaraciones al código.
2. La versión de en la Sección 4.7 no es muy precisa porque la aproximación lineal del círculo siempre está
fuera del círculo verdadero. Como resultado, la tortuga termina a unos pocos píxeles del destino correcto.
Mi solución muestra una forma de reducir el efecto de este error. Lea el código y vea si tiene sentido para
usted. Si dibuja un diagrama, puede ver cómo funciona.
Ejercicio 4.2. Escriba un conjunto general apropiado de funciones que puedan dibujar flores como en la figura 4.1.
Solución: , también requiere
.
Ejercicio 4.3. Escriba un conjunto general apropiado de funciones que puedan dibujar formas como en la figura 4.2.
Solución: .
Ejercicio 4.4. Las letras del alfabeto se pueden construir a partir de un número moderado de elementos básicos,
como líneas verticales y horizontales y algunas curvas. Diseñe un alfabeto que se pueda dibujar con un número
mínimo de elementos básicos y luego escriba funciones que dibujen las letras.
Debe escribir una función para cada letra, con funciones de nombres , , etc., y pon tu . Puede
en un archivo llamado descargar una "máquina de escribir tortuga" para
ayudarlo a probar su código.
Machine Translated by Google
38 Capítulo 4. Caso de estudio: diseño de interfaz
Puede obtener una solución de ; también requiere
.
Ejercicio 4.5. Lea sobre espirales en ; luego escriba un programa que dibuje una espiral de Arquímedes (o
uno de los otros tipos). Solución:
.
Machine Translated by Google
Capítulo 5
Condicionales y recursión
El tema principal de este capítulo es la sentencia, que ejecuta código diferente dependiendo del estado del
programa. Pero primero quiero presentar dos nuevos operadores: división de piso y módulo.
5.1 División de piso y módulo
El operador de división de piso , divide dos números y los redondea a un número entero. Por ejemplo,
suponga que el tiempo de ejecución de una película es de 105 minutos. Es posible que desee saber cuánto
tiempo es eso en horas. La división convencional devuelve un número de coma flotante:
Pero normalmente no escribimos las horas con puntos decimales. La división de piso devuelve el número
entero de horas, redondeando hacia abajo:
Para obtener el resto, podría restar una hora en minutos:
Una alternativa es utilizar el operador de módulo, que divide dos números y devuelve el resto.
El operador de módulo es más útil de lo que parece. Por ejemplo, puede verificar si un número es divisible
por otro; si es cero, entonces es divisible por .
Machine Translated by Google
40 Capítulo 5. Condicionales y recursión
Además, puede extraer el dígito o dígitos más a la derecha de un número. Por ejemplo, produce
el dígito más a la derecha de (en base 10). De manera similar, produce los dos últimos dígitos.
Si está utilizando Python 2, la división funciona de manera diferente. El operador de división, realiza una
división mínima si ambos operandos son enteros y una división de punto flotante si cualquiera de los operandos es
a .
5.2 Expresiones booleanas
Una expresión booleana es una expresión que es verdadera o falsa. Los siguientes ejemplos usan el
operador , que compara
dos operandos y produce si son iguales y si no:
y son valores especiales que pertenecen al tipo ; no son cadenas:
El operador es uno de los operadores relacionales; los otros son:
Aunque estas operaciones probablemente le resulten familiares, los símbolos de Python son diferentes
de los símbolos matemáticos. Un error común es usar un solo signo igual () en lugar de un doble signo
igual (). Recuerda que es un operador de asignación y es un operador relacional. No existe tal cosa
como o .
5.3 Operadores lógicos
Hay tres operadores lógicos: operadores , , y . La semántica (significado) de estos es verdadera
es similar a su significado en inglés. Por ejemplo, solo si es mayor que
0 y menor que 10.
es verdadero si una o ambas condiciones son verdaderas, es decir, si el número
es divisible por 2 o 3.
el falso, es decir, si es menor o igual que .
Estrictamente hablando, los operandos de los operadores lógicos deberían ser expresiones booleanas, pero
Python no es muy estricto. Cualquier número distinto de cero se interpreta como :
Machine Translated by Google
5.4. ejecución condicional 41
Esta flexibilidad puede ser útil, pero tiene algunas sutilezas que pueden resultar confusas.
Es posible que desee evitarlo (a menos que sepa lo que está haciendo).
5.4 Ejecución condicional
Para escribir programas útiles, casi siempre necesitamos la capacidad de verificar las condiciones y cambiar el
comportamiento del programa en consecuencia. Las declaraciones condicionales nos dan esta habilidad. La
forma más simple es la declaración:
La expresión booleana posterior se denomina condición. Si es verdadero, se ejecuta la instrucción con sangría.
Si no, no pasa nada.
Las declaraciones tienen la misma estructura que las definiciones de función: un encabezado seguido de un
cuerpo sangrado. Las declaraciones como esta se llaman declaraciones compuestas.
No hay límite en la cantidad de declaraciones que pueden aparecer en el cuerpo, pero tiene que haber al menos
una. Ocasionalmente, es útil tener un cuerpo sin declaraciones (generalmente como un guardián del código que
aún no ha escrito). En ese caso, puede usar la declaración, que no hace nada.
5.5 Ejecución alternativa
Una segunda forma de la declaración es "ejecución alternativa", en la que hay dos posibilidades y la condición
determina cuál se ejecuta. La sintaxis se ve así:
Si el resto cuando se divide por 2 es 0, entonces sabemos que es par y el programa muestra un mensaje
apropiado. Si la condición es falsa, se ejecuta el segundo conjunto de sentencias. Dado que la condición debe
ser verdadera o falsa, se ejecutará exactamente una de las alternativas. Las alternativas se denominan ramas,
porque son ramas en el flujo de ejecución.
5.6 Condicionales encadenados
A veces hay más de dos posibilidades y necesitamos más de dos ramas.
Una forma de expresar un cálculo como ese es un condicional encadenado:
Machine Translated by Google
42 Capítulo 5. Condicionales y recursión
es una abreviatura de "else if". Nuevamente, se ejecutará exactamente una rama. No hay límite de
el número de no declaraciones. Si hay una cláusula, tiene que estar al final, pero no
tiene que ser uno.
Cada condición se comprueba en orden. Si la primera es falsa, se comprueba la siguiente, y así sucesivamente.
Si uno de ellos es verdadero, se ejecuta la rama correspondiente y finaliza la instrucción. Incluso si más de una
condición es verdadera, solo se ejecuta la primera rama verdadera.
5.7 Condicionales anidados
Un condicional también se puede anidar dentro de otro. Podríamos haber escrito el ejemplo en la sección anterior
así:
El condicional externo contiene dos ramas. La primera rama contiene una declaración simple.
La segunda rama contiene otra declaración, que tiene dos ramas propias.
Esas dos ramas son declaraciones simples, aunque también podrían haber sido declaraciones condicionales.
Aunque la sangría de las declaraciones hace que la estructura sea aparente, los condicionales anidados se
vuelven difíciles de leer muy rápidamente. Es una buena idea evitarlos cuando
poder.
Los operadores lógicos a menudo proporcionan una forma de simplificar declaraciones condicionales anidadas.
Por ejemplo, podemos reescribir el siguiente código usando un solo condicional:
La declaración se ejecuta solo si superamos ambos condicionales, por lo que podemos obtener el mismo efecto
con el operador:
Machine Translated by Google
5.8. recursividad 43
Para este tipo de condición, Python ofrece una opción más concisa:
5.8 Recursividad
Es legal que una función llame a otra; también es legal que una función se llame a sí misma.
Puede que no sea obvio por qué eso es algo bueno, pero resulta ser una de las cosas más
mágicas que puede hacer un programa. Por ejemplo, observe la siguiente función:
Si es 0 o negativo, emite la palabra "Blastoff!" De lo contrario, genera una salida y luego se llama —a sí
una función llamada mismo— pasándolo como un argumento.
¿Qué pasa si llamamos a esta función así?
La ejecución de comienza con el valor 3 y luego , y dado que es mayor que 0, genera el
se llama a sí misma...
La ejecución de comienza con la salida del , y como es mayor que 0,
valor 2 y luego se llama a sí misma...
La ejecución de comienza con 0, genera , y como es mayor
el valor 1 y luego se llama a sí mismo...
La ejecución de comienza con no más de , y puesto que es
0, genera la palabra "¡Blastoff!" y luego regresa.
El eso tiene devoluciones.
El eso tiene devoluciones.
El eso tiene devoluciones.
Y luego estás de vuelta . Entonces, la salida total se ve así:
Una función que se llama a sí misma es recursiva; el proceso de ejecución se llama recursividad.
Como otro ejemplo, podemos escribir una función que imprima una cadena veces.
Machine Translated by Google
44 Capítulo 5. Condicionales y recursión
__principal__
cuenta regresiva norte 3
cuenta regresiva norte 2
cuenta regresiva norte 1
cuenta regresiva norte
0
Figura 5.1: Diagrama de pila.
Si la declaración de retorno sale de la función. El flujo de ejecución vuelve inmediatamente a la persona
que llama y las líneas restantes de la función no se ejecutan.
El resto de la función es similar a : se muestra y luego se llama a sí mismo para
mostrar n − 1 veces adicionales. Entonces, el número de líneas de salida es lo que s, uma
a .
Para ejemplos simples como este, probablemente sea más fácil usar un bucle. Pero veremos ejemplos más
adelante que son difíciles de escribir con un bucle y fáciles de escribir con recursividad, por lo que es bueno
comenzar temprano.
5.9 Diagramas de pila para funciones recursivas
En la Sección 3.9, usamos un diagrama de pila para representar el estado de un programa durante una llamada
de función. El mismo tipo de diagrama puede ayudar a interpretar una función recursiva.
Cada vez que se llama a una función, Python crea un marco para contener las variables y parámetros
locales de la función. Para una función recursiva, puede haber más de un cuadro en la pila al mismo
tiempo.
La Figura 5.1 muestra un diagrama de pila para llamado con .
Como de costumbre, la parte superior de la pila es el . Está vacío porque no lo hicimos.
marco para crear cualquier variable o pasarle argumentos.
Los cuatro los marcos tienen diferentes valores para el parámetro. La parte inferior del se
se apilan, donde , llama el caso base. No hace una llamada recursiva, por lo que no hay
hay más fotogramas.
llamado
Como ejercicio, dibuje un diagrama de pila para y . Luego e scriba ucna
onfunción llamada que tome un
objeto de función y un número, como argumentos, y que llame a la función dada veces.
5.10 Recurrencia infinita
Si una recursión nunca llega a un caso base, continúa haciendo llamadas recursivas para siempre y el
programa nunca termina. Esto se conoce como recursividad infinita y generalmente no es una buena
idea. Aquí hay un programa mínimo con una recursividad infinita:
Machine Translated by Google
5.11. Entrada de teclado 45
En la mayoría de los entornos de programación, un programa con recursividad infinita en realidad no se
ejecuta para siempre. Python informa un mensaje de error cuando se alcanza la profundidad de recursión máxima:
Este rastreo es un poco más grande que el que vimos en el capítulo anterior. Cuando ocurre el error,
¡hay 1000 marcos en la pila!
Si encuentra una recursividad infinita por accidente, revise su función para confirmar que hay un caso
base que no realiza una llamada recursiva. Y si hay un caso base, verifique si tiene la garantía de
alcanzarlo.
5.11 Entrada de teclado
Los programas que hemos escrito hasta ahora no aceptan ninguna entrada del usuario. Simplemente hacen
lo mismo cada vez.
Python proporciona una función integrada llamada que detiene el programa y espera a que el usuario
o y devuelve
escriba algo. Cuando el usuario presiona, el programa se reanuda , lo que el usuario escribió
como una cadena. En Python 2, la misma función se llama
.
Antes de obtener información del usuario, es una buena idea imprimir un aviso que le diga al usuario qué
escribir. puede tomar un aviso como argumento:
La secuencia al final del indicador representa una nueva línea, que es un carácter especial que provoca
un salto de línea. Es por eso que la entrada del usuario aparece debajo del aviso.
Si espera que el usuario escriba un número entero, puede intentar convertir el valor de retorno a :
Machine Translated by Google
46 Capítulo 5. Condicionales y recursión
Pero si el usuario escribe algo que no sea una cadena de dígitos, obtendrá un error:
Veremos cómo manejar este tipo de error más adelante.
5.12 Depuración
Cuando ocurre un error de sintaxis o de tiempo de ejecución, el mensaje de error contiene mucha
información, pero puede ser abrumador. Las partes más útiles suelen ser:
• Qué tipo de error fue, y
• Dónde ocurrió.
Los errores de sintaxis suelen ser fáciles de encontrar, pero hay algunas trampas. Los errores de espacios en blanco
pueden ser complicados porque los espacios y las pestañas son invisibles y estamos acostumbrados a ignorarlos.
En este ejemplo, el problema es que la segunda línea está sangrada por un espacio. Pero el error que
mensaje al problema , es engañoso. En general, los mensajes de error indican dónde apunta el
que se descubrió, pero el error real puede estar antes en el código, a veces en una línea anterior.
Lo mismo ocurre con los errores de tiempo de ejecución. Suponga que está tratando de calcular una
relación señalruido en decibelios. La fórmula es SNRdb = 10 log10(Pseñal/Pruido). En Python, podrías
escribir algo como esto:
Cuando ejecuta este programa, obtiene una excepción:
El mensaje de error indica la línea 5, pero no hay nada de malo en esa línea. Para encontrar el error
real, puede ser útil imprimir el valor que resulta ser 0. El problema e,stá en la línea 4, que usa división
de piso en lugar de división de punto flotante.
Debe tomarse el tiempo para leer los mensajes de error cuidadosamente, pero no asuma que todo lo
que dicen es correcto.
Machine Translated by Google
5.13. Glosario 47
5.13 Glosario
división de piso: Un operador, denotado , que divide dos números y redondea hacia abajo
(hacia el infinito negativo) a un número entero.
operador de módulo: un operador, indicado con un signo de porcentaje (), que funciona con
números enteros y devuelve el resto cuando un número se divide por otro.
expresión booleana: una expresión cuyo valor es o .
operador relacional: Uno de los operadores que compara sus operandos: , , , , ,y
.
operador lógico: Uno de los operadores que combina expresiones booleanas: , , y
.
sentencia condicional: Una sentencia que controla el flujo de ejecución dependiendo de alguna
condición.
condición: La expresión booleana en una declaración condicional que determina qué
carreras de rama.
declaración compuesta: una declaración que consta de un encabezado y un cuerpo. El encabezado termina
con dos puntos (:). El cuerpo está sangrado en relación con el encabezado.
rama: Una de las secuencias alternativas de sentencias en una sentencia condicional.
Condicional encadenado: una declaración condicional con una serie de ramas alternativas.
Condicional anidado: una declaración condicional que aparece en una de las ramas de otra
declaración condicional.
declaración de retorno: una declaración que hace que una función finalice inmediatamente y regrese a la
llamador.
recursividad: El proceso de llamar a la función que se está ejecutando actualmente.
caso base: una rama condicional en una función recursiva que no realiza una llamada recursiva.
recursión infinita: Una recursión que no tiene un caso base, o nunca lo alcanza. Evento
aliado, una recursividad infinita provoca un error de tiempo de ejecución.
5.14 Ejercicios
Ejercicio 5.1. El módulo proporciona una función, también denominada hora , que devuelve la corriente
media de Greenwich en "la época", que es una hora arbitraria utilizada como punto de referencia. En los sistemas
UNIX, la época es el 1 de enero de 1970.
Escriba un script que lea la hora actual y la convierta en una hora del día en horas, minutos y
segundos, más el número de días desde la época.
Machine Translated by Google
48 Capítulo 5. Condicionales y recursión
Ejercicio 5.2. El último teorema de Fermat dice que no hay números enteros positivos a, b y c tales que
n
un + segundo
norte
n = c
para cualquier valor de n mayor que 2.
a norte
+ segundo
norte
= do norte
el programa debería imprimir, "¡Santo cielo, Fermat estaba equivocado!" De lo contrario, el programa
debería imprimir "No, eso no funciona".
,
2. Escriba una función que solicite al usuario que ingrese valores para , y números , los convierte en
enteros, y los use para verificar si violan el teorema de Fermat.
Ejercicio 5.3. Si le dan tres palos, es posible que pueda o no colocarlos en un triángulo.
Por ejemplo, si uno de los palos mide 12 pulgadas de largo y los otros dos miden una pulgada de largo, no podrá
hacer que los palos cortos se junten en el medio. Para tres longitudes cualesquiera, hay una prueba simple para
ver si es posible formar un triángulo:
Si alguna de las tres longitudes es mayor que la suma de las otras dos, entonces no se puede
formar un triángulo. De lo contrario, puedes. (Si la suma de dos longitudes es igual a la tercera,
forman lo que se llama un triángulo "degenerado").
1. Escriba una función llamada que tome tres números enteros como argumentos y que imprima "Sí" o "No",
dependiendo de si puede o no formar un triángulo con palos con las longitudes dadas.
2. Escriba una función que solicite al usuario que ingrese tres longitudes de palo, las convierta en números enteros,
y usos para comprobar si los palos con las longitudes dadas pueden formar un triángulo.
Ejercicio 5.4. ¿Cuál es la salida del siguiente programa? Dibuje un diagrama de pila que muestre el estado del
programa cuando imprime el resultado.
1. ¿Qué pasaría si llamaras a esta función así: ?
2. Escriba una cadena de documentación que explique todo lo que alguien necesitaría saber para usar esta
función (y nada más).
Los siguientes ejercicios utilizan el módulo, descrito en el Capítulo 4:
Ejercicio 5.5. Lea la siguiente función y vea si puede averiguar lo que hace (vea los ejemplos en el Capítulo 4).
Luego ejecútelo y vea si lo hizo bien.
Machine Translated by Google
5.14. Ejercicios 49
Figura 5.2: Una curva de Koch.
Ejercicio 5.6. La curva de Koch es un fractal que se parece a la figura 5.2. Para dibujar una curva de Koch con longitud x, todo lo que
tienes que hacer es
1. Dibuja una curva de Koch con longitud x/3.
2. Gire a la izquierda 60 grados.
3. Dibujar una curva de Koch con longitud x/3.
4. Gire a la derecha 120 grados.
5. Dibuja una curva de Koch con longitud x/3.
6. Gire a la izquierda 60 grados.
7. Dibuja una curva de Koch con longitud x/3.
La excepción es si x es menor que 3: en ese caso, puedes dibujar una línea recta con longitud x.
1. Escribe una función llamada que toma como parámetros una tortuga y una longitud, y que utiliza el
tortuga para dibujar una curva de Koch con la longitud dada.
2. Escribe una función llamada copo que dibuja tres curvas de Koch para hacer el contorno de un
de nieve.
Solución: .
3. La curva de Koch se puede generalizar de varias formas. Vea ejemplos e
implemente su favorito.
Machine Translated by Google
50 Capítulo 5. Condicionales y recursión
Machine Translated by Google
Capítulo 6
Funciones fructíferas
Muchas de las funciones de Python que hemos usado, como las funciones matemáticas, producen
valores de retorno. Pero todas las funciones que hemos escrito son nulas: tienen un efecto, como
imprimir un valor o mover una tortuga, pero no tienen un valor de retorno. En este capítulo aprenderás a
escribir funciones fructíferas.
6.1 Valores devueltos
Llamar a la función genera un valor de retorno, que normalmente asignamos a una variable o usamos
como parte de una expresión.
Las funciones que hemos escrito hasta ahora son nulas. Hablando casualmente, no tienen valor de
retorno; más precisamente, su valor de retorno es .
En este capítulo, (finalmente) vamos a escribir funciones fructíferas. El primer ejemplo es el que ,
devuelve el área de un círculo con el radio dado:
Por otro lado, las variables temporales como pueden facilitar la depuración.
A veces es útil tener varias declaraciones de retorno, una en cada rama de un condicional:
Machine Translated by Google
52 Capítulo 6. Funciones fructíferas
Dado que estos Las declaraciones están en un condicional alternativo, solo se ejecuta uno.
Tan pronto como se ejecuta una declaración de devolución, la función finaliza sin ejecutar ninguna
declaración posterior. El código que aparece después de una instrucción, o cualquier otro lugar al que nunca
llegue el flujo de ejecución, se denomina código muerto.
En una función fructífera, es una buena idea asegurarse de que todos los caminos posibles a través del
programa lleguen a una declaración. Por ejemplo:
Esta función es incorrecta porque si pasa a ser 0, ninguna de las condiciones es verdadera y la
función termina sin llegar a una declaración. Si el flujo de ejecución llega al final de una función, el
valor devuelto es que no es el valor absoluto
, de 0.
Por cierto, Python proporciona una función integrada llamada que calcula valores absolutos.
6.2 Desarrollo incremental
A medida que escribe funciones más grandes, es posible que dedique más tiempo a la depuración.
Para lidiar con programas cada vez más complejos, es posible que desee probar un proceso
llamado desarrollo incremental. El objetivo del desarrollo incremental es evitar largas sesiones de
depuración agregando y probando solo una pequeña cantidad de código a la vez.
Como ejemplo, suponga que desea encontrar la distancia entre dos puntos, dada por las
coordenadas (x1, y1) y (x2, y2). Por el teorema de Pitágoras, la distancia es:
distancia = (x2 − x1) 2 + (y2 − y1) 2
El primer paso es considerar cómo debería verse una función en Python. En otras palabras, ¿cuáles
son las entradas (parámetros) y cuál es la salida (valor de retorno)?
En este caso, las entradas son dos puntos, que puedes representar usando cuatro números. El
valor devuelto es la distancia representada por un valor de coma flotante.
Inmediatamente puede escribir un esquema de la función:
Machine Translated by Google
6.2. Desarrollo incremental 53
Obviamente, esta versión no calcula distancias; siempre devuelve cero. Pero es sintácticamente
correcto y funciona, lo que significa que puedes probarlo antes de complicarlo más.
Para probar la nueva función, llámela con argumentos de ejemplo:
Elegí estos valores para que la distancia horizontal sea 3 y la distancia vertical sea 4; así el
resultado es 5, la hipotenusa de un triángulo rectángulo 345. Al probar una función, es útil
saber la respuesta correcta.
En este punto, hemos confirmado que la función es sintácticamente correcta y podemos
comenzar a agregar código al cuerpo. Un siguiente paso razonable es encontrar las diferencias
x2 − x1 e y2 − y1. La próxima versión almacena esos valores en variables temporales y los imprime.
y
Si la función funciona, debería mostrarse. Si es así, sabemos .
que la función
obtiene los
argumentos correctos y realiza el primer cálculo correctamente. Si no, solo hay unas pocas
líneas para verificar.
A continuación calculamos la suma de los cuadrados de y :
Nuevamente, ejecutaría el programa en esta etapa y verificaría la salida (que debería ser 25).
Finalmente, puede usar para calcular y devolver el resultado:
Si eso funciona correctamente, ya está. De lo contrario, es posible que desee imprimir el valor de antes
de la declaración de devolución.
La versión final de la función no muestra nada cuando se ejecuta; solo devuelve un valor. Las
declaraciones que escribimos son útiles para la depuración, pero una vez que la función
funcione, debe eliminarlas. Este tipo de código se llama andamiaje porque es útil para construir
el programa pero no es parte del producto final.
Cuando comience, debe agregar solo una línea o dos de código a la vez. A medida que adquiera más
experiencia, es posible que se encuentre escribiendo y depurando fragmentos más grandes. De cualquier
manera, el desarrollo incremental puede ahorrarle mucho tiempo de depuración.
Los aspectos clave del proceso son:
Machine Translated by Google
54 Capítulo 6. Funciones fructíferas
1. Comience con un programa de trabajo y realice pequeños cambios incrementales. En cualquier momento, si
hay un error, debe tener una buena idea de dónde está.
2. Use variables para contener valores intermedios para que pueda mostrarlos y verificarlos.
3. Una vez que el programa esté funcionando, es posible que desee eliminar algunos de
los andamios o consolidar varias declaraciones en expresiones compuestas, pero solo
si no dificulta la lectura del programa.
Como ejercicio, use el desarrollo incremental para escribir una función que devuelva la longitud de la
hipotenusa de un triángulo rectángulo dadas las longitudes de los otros dos catetos como argumentos.
Registre cada etapa del proceso de desarrollo a medida que avanza.
6.3 Composición
Como ya debería esperar, puede llamar a una función desde otra. Como ejemplo,
escribiremos una función que toma dos puntos, el centro del círculo y un punto en el
perímetro, y calcula el área del círculo.
Suponga que el punto central está almacenado en las variables y ,y el punto del perímetro
está en y . El primer paso es encontrar el radio del círculo, que es la distancia entre los dos
puntos. Acabamos de escribir una función, , eso hace eso:
El siguiente paso es encontrar el área de un círculo con ese radio; acabamos de escribir eso, también:
Encapsulando estos pasos en una función, obtenemos:
y el desarrollo y la depuración, pero una vez que el programa
Las variables temporales son útiles para
está funcionando, podemos hacerlo más conciso componiendo las llamadas a funciones:
6.4 Funciones booleanas
Las funciones pueden devolver valores booleanos, lo que suele ser conveniente para ocultar pruebas complicadas
en funciones secundarias. Por ejemplo:
Machine Translated by Google
6.5. Más recursividad 55
Es común dar nombres de funciones booleanas que suenen como preguntas de sí/no; devuelve cualquiera
para indicar si es divisible
o por .
Aquí hay un ejemplo:
El resultado del operador es un booleano, por lo que podemos escribir la función de manera más concisa
devolviéndola directamente:
Las funciones booleanas se utilizan a menudo en declaraciones condicionales:
Puede ser tentador escribir algo como:
Pero la comparación adicional es innecesaria.
6.5 Más recursividad
Solo hemos cubierto un pequeño subconjunto de Python, pero es posible que le interese saber que este
subconjunto es un lenguaje de programación completo, lo que significa que todo lo que se puede calcular se
puede expresar en este lenguaje. Cualquier programa que se haya escrito podría reescribirse usando solo
las características del lenguaje que ha aprendido hasta ahora (en realidad, necesitaría algunos comandos
para controlar dispositivos como el mouse, discos, etc., pero eso es todo).
Demostrar esa afirmación es un ejercicio no trivial realizado por primera vez por Alan Turing, uno de los
primeros científicos informáticos (algunos dirían que era matemático, pero muchos de los primeros científicos
informáticos comenzaron como matemáticos). En consecuencia, se conoce como la Tesis de Turing. Para
una discusión más completa (y precisa) de la tesis de Turing, recomiendo el libro de Michael Sipser
Introducción a la teoría de la computación.
Para darle una idea de lo que puede hacer con las herramientas que ha aprendido hasta ahora, evaluaremos
algunas funciones matemáticas definidas recursivamente. Una definición recursiva es similar a una definición
circular, en el sentido de que la definición contiene una referencia a la cosa que se define. Una definición
verdaderamente circular no es muy útil:
vorpal: Un adjetivo usado para describir algo que es vorpal.
Si vio esa definición en el diccionario, es posible que se moleste. Por otro lado, si buscó la definición de la
función factorial, denotada con el símbolo !, podría obtener algo como esto:
0! = 1
¡norte! = n(n − 1)!
Machine Translated by Google
56 Capítulo 6. Funciones fructíferas
Esta definición dice que el factorial de 0 es 1, y el factorial de cualquier otro valor, n, es n multiplicado
por el factorial de n − 1.
¡Así que 3! es 3 por 2!, que es 2 por 1!, que es 1 por 0!. ¡Poniéndolo todo junto, 3! es igual a 3 por 2
por 1 por 1, que es 6.
Si puede escribir una definición recursiva de algo, puede escribir un programa Python para evaluarlo.
El primer paso es decidir cuáles deben ser los parámetros. En este caso debe quedar claro que toma
un número entero:
Si el argumento resulta ser 0, todo lo que tenemos que hacer es devolver 1:
De lo contrario, y esta es la parte interesante, tenemos que hacer una llamada recursiva para
encontrar el factorial de n − 1 y luego multiplicarlo por n:
El flujo de ejecución de este programa es similar al flujo de lo que en la Sección 5.8. Si
llamamos con el valor 3:
Como 3 no es 0, tomamos la segunda rama y calculamos el factorial de ...
Como 2 no es 0, tomamos la segunda rama y calculamos el factorial de ...
Como 1 no es 0, tomamos la segunda rama y calculamos el factorial de
...
Dado que 0 es igual a 0, tomamos la primera rama y devolvemos 1
sin realizar más llamadas recursivas.
El valor devuelto, 1, se multiplica por n, que es 1, y se devuelve el resultado.
El valor devuelto, 1, se multiplica por n, que es 2, y se devuelve el resultado.
El valor de retorno (2) se multiplica por n, que es 3, y el resultado, 6, se convierte en el valor de
retorno de la llamada de función que inició todo el proceso.
La figura 6.1 muestra cómo se ve el diagrama de pila para esta secuencia de llamadas a funciones.
Los valores devueltos se muestran pasando de vuelta a la pila. En cada marco, el valor devuelto es
el valor del cual es el producto d, e y .
En el último cuadro, las variables locales y no existen, porque la rama
que las crean no se ejecutan.
Machine Translated by Google
6.6. Salto de fe 57
__principal__
6
factorial norte 3 recursivo 2 resultado 6
2
factorial norte 2 recursivo 1 resultado 2
1
factorial norte 1 recursivo 1 resultado 1
1
factorial norte
0
Figura 6.1: Diagrama de pila.
6.6 Acto de fe
Seguir el flujo de ejecución es una forma de leer programas, pero puede volverse abrumador
rápidamente. Una alternativa es lo que yo llamo el “salto de fe”. Cuando llega a una llamada de
función, en lugar de seguir el flujo de ejecución, asume que la función funciona correctamente y
devuelve el resultado correcto.
De hecho, ya está practicando este acto de fe cuando utiliza las funciones integradas. Cuando llama
o
a , no examina los cuerpos
de esas funciones. Simplemente asume que funcionan porque las
personas que escribieron las funciones integradas eran buenos programadores.
Lo mismo es cierto cuando llamas a una de tus propias funciones. Por ejemplo, en la Sección 6.4,
escribimos una función llamada que determina si un número es divisible por otro. Una vez que nos
hayamos convencido de que esta función es correcta, examinando el código y probando, podemos
usar la función sin volver a mirar el cuerpo.
Lo mismo ocurre con los programas recursivos. Cuando llegue a la llamada recursiva, en lugar de
seguir el flujo de ejecución, debe suponer que la llamada recursiva funciona (devuelve el resultado
correcto) y luego pregúntese: "Suponiendo que puedo encontrar el factorial de n − 1, ¿ puedo
calcular el factorial de n? Está claro que puedes, multiplicando por n.
Por supuesto, es un poco extraño asumir que la función funciona correctamente cuando no has
terminado de escribirla, ¡pero por eso se llama un acto de fe!
6.7 Un ejemplo más
Después , el ejemplo más común de una función matemática definida recursivamente
de ción es , que tiene la siguiente definición (ver
):
fibonacci(0) = 0
fibonacci(1) = 1
fibonacci(n) = fibonacci(n − 1) + fibonacci(n − 2)
Traducido a Python, se ve así:
Machine Translated by Google
58 Capítulo 6. Funciones fructíferas
Si intenta seguir el flujo de ejecución aquí, incluso para valores bastante pequeños de n, su cabeza explota.
Pero de acuerdo con el acto de fe, si asume que las dos llamadas recursivas funcionan correctamente,
entonces está claro que obtiene el resultado correcto al sumarlas.
6.8 Tipos de comprobación
¿Qué pasa si llamamos? y darle 1.5 como argumento?
Parece una recursividad infinita. ¿Como puede ser? La función tiene un caso base: cuando . Pero si no es
un número entero, podemos perder el caso base y repetir para siempre.
En la primera llamada recursiva, el valor de es 0,5. En el siguiente, es 0.5. A partir de ahí, se vuelve más
pequeño (más negativo), pero nunca será 0.
Tenemos dos opciones. Podemos intentar generalizar la función para que funcione con números de punto
flotante, o podemos comprobar el tipo de su argumento. La primera opción se llama función gamma y está un
poco más allá del alcance de este libro. Así que vamos a por el segundo.
Podemos usar la función incorporada para verificar el tipo del argumento. Mientras estamos en eso, también
podemos asegurarnos de que el argumento sea positivo:
El primer caso base maneja números no enteros; el segundo maneja enteros negativos. En ambos casos, el
programa imprime un mensaje de error y regresa para indicar que algo salió mal:
Machine Translated by Google
6.9. depuración 59
Si superamos ambas comprobaciones, sabemos que n es un número entero no negativo, por lo que podemos
probar que la recursividad termina.
Este programa demuestra un patrón a veces llamado guardián. Los dos primeros condicionales actúan como
guardianes, protegiendo el código que sigue de valores que podrían causar un error. Los guardianes permiten
probar la exactitud del código.
En la Sección 11.4 veremos una alternativa más flexible a la impresión de un mensaje de error: generar una
excepción.
6.9 Depuración
Dividir un programa grande en funciones más pequeñas crea puntos de control naturales para la depuración.
Si una función no funciona, hay tres posibilidades a considerar:
• Hay algo mal con los argumentos que recibe la función; una condición previa
es violado
• Hay algún problema con la función; se viola una poscondición.
• Hay algún problema con el valor de retorno o la forma en que se utiliza.
Para descartar la primera posibilidad, puede agregar una declaración al comienzo de la función y mostrar los
valores de los parámetros (y tal vez sus tipos). O puede escribir código que compruebe explícitamente las
condiciones previas.
Si los parámetros se ven bien, agregue una declaración antes de cada declaración y muestre el valor de retorno.
Si es posible, verifique el resultado a mano. Considere llamar a la función con valores que faciliten la verificación
del resultado (como en la Sección 6.2).
Si la función parece estar funcionando, mire la llamada a la función para asegurarse de que el valor devuelto se
esté usando correctamente (¡o no se esté usando!).
Agregar declaraciones de impresión al principio y al final de una función puede ayudar a que el flujo de ejecución
sea más visible. Por ejemplo, aquí hay una versión de declaraciones con impresión:
es una cadena de caracteres de espacio que controla la sangría de la salida. Aquí está el resultado de
:
Machine Translated by Google
60 Capítulo 6. Funciones fructíferas
Si está confundido acerca del flujo de ejecución, este tipo de salida puede ser útil. Se necesita algo de tiempo para
desarrollar un andamiaje eficaz, pero un poco de andamiaje puede ahorrar una gran cantidad de depuración.
6.10 Glosario
variable temporal: una variable utilizada para almacenar un valor intermedio en un cálculo complejo
ción
código muerto: Parte de un programa que nunca se puede ejecutar, a menudo porque aparece después de un
declaración.
desarrollo incremental: un plan de desarrollo de programa destinado a evitar la depuración por
agregando y probando solo una pequeña cantidad de código a la vez.
scaffolding: Código que se utiliza durante el desarrollo del programa, pero no es parte de la final
versión.
guardián: un patrón de programación que usa una declaración condicional para verificar y
dle circunstancias que podrían causar un error.
6.11 Ejercicios
Ejercicio 6.1. Dibuje un diagrama de pila para el siguiente programa. ¿Qué imprime el programa?
Machine Translated by Google
6.11. Ejercicios 61
Ejercicio 6.2. La función de Ackermann, A(m, n), se define:
n + 1 si m = 0
A(m, n) = A(m − 1, 1) si m > 0 y n = 0
A(m − 1, A(m, n − 1)) si m > 0 y n > 0.
Ver . Escriba una función llamada que evalúe la función de Ackermann. Usa tu función para evaluar cuál
debería ser 125. ¿Qué sucede con valores más grandes de y ? Solución: ,
.
Ejercicio 6.3. Un palíndromo es una palabra que se escribe igual al derecho y al revés, como "noon" y "redivider".
Recursivamente, una palabra es un palíndromo si la primera y la última letra son iguales y la del medio es un
palíndromo.
Las siguientes son funciones que toman un argumento de cadena y devuelven la primera, la última y la letra del medio:
Veremos cómo funcionan en el Capítulo 8.
1. Escriba estas funciones en un archivo llamado y pruébelas. ¿Qué pasa si llamas con una cadena de dos
letras? ¿Una carta? ¿Qué pasa con la cadena vacía, que está escrita y no contiene letras?
2. Escribe una función llamada que toma un argumento de cadena y devuelve si es lo
es un palíndromo y para contrario. Recuerda que puedes usar la función incorporada
verificar la longitud de una cadena.
Solución: .
Ejercicio 6.4. Un número, a, es una potencia de b si es divisible por b y a/b es una potencia de b. Escriba una
función llamada que tome parámetros y devuelva si es una potencia de . Nota: tendrás que pensar en el caso
base.
Ejercicio 6.5. El máximo común divisor (MCD) de a y b es el número más grande que divide a ambos sin resto.
Una forma de encontrar el MCD de dos números se basa en la observación de que si r es el resto cuando a se
divide entre b, entonces mcd(a, b) = mcd(b,r). Como caso base, podemos usar mcd(a, 0) = a.
Escribe una función llamada que toma parámetros y devuelve su máximo común divisor.
Crédito: Este ejercicio se basa en un ejemplo de la Estructura e Interpretación de Programas de Computadora
de Abelson y Sussman.
Machine Translated by Google
62 Capítulo 6. Funciones fructíferas
Machine Translated by Google
Capítulo 7
Iteración
Este capítulo trata sobre la iteración, que es la capacidad de ejecutar un bloque de sentencias repetidamente.
Vimos una especie de iteración, usando recursividad, en la Sección 5.8. Vimos otro tipo, usando un bucle, en
la Sección 4.2. En este capítulo veremos otro tipo más, usando una instrucción.
Pero primero quiero hablar un poco más sobre la asignación de variables.
7.1 Reasignación
Como habrá descubierto, es legal realizar más de una asignación a la misma variable. Una nueva asignación
hace que una variable existente se refiera a un nuevo valor (y deje de referirse al valor anterior).
La primera vez que mostramos , su valor es 5; la segunda vez, su valor es 7.
La figura 7.1 muestra cómo se ve la reasignación en un diagrama de estado.
En este punto quiero abordar una fuente común de confusión. Debido a que Python usa el signo igual () para
la asignación, es tentador interpretar una declaración como una proposición matemática como matemático
de igualdad; es decir, la afirmación de que y son iguales. Pero esta interpretación es incorrecta.
Primero, la igualdad es una relación simétrica y la asignación no lo es. Por ejemplo, en matemáticas, si a =
7 entonces 7 = a. Pero en Python, la declaración es es legal y
no.
Además, en matemáticas, una proposición de igualdad es verdadera o falsa para todos los tiempos. Si a = b
ahora, entonces a siempre será igual a b. En Python, una declaración de asignación puede hacer que dos
variables sean iguales, pero no tienen por qué permanecer así:
Machine Translated by Google
64 Capítulo 7. Iteración
5
X
7
Figura 7.1: Diagrama de estado.
La tercera línea cambia el valor de pero no cambia el valor de más largo igual. , por lo que no son
La reasignación de variables suele ser útil, pero debe usarla con precaución. Si los valores de las
variables cambian con frecuencia, el código puede resultar difícil de leer y depurar.
7.2 Actualización de variables
Un tipo común de reasignación es una actualización, donde el nuevo valor de la variable depende
del antiguo.
Esto significa "obtener el valor actual de , agregue uno y luego actualice con el nuevo valor ".
Si intenta actualizar una variable que no existe, obtiene un error, porque Python evalúa el lado
derecho antes de asignar un valor a:
Antes de que pueda actualizar una variable, debe inicializarla , generalmente con una asignación
simple:
Actualizar una variable sumando 1 se denomina incremento; restar 1 se llama decremento.
7.3 El declaración
Las computadoras se utilizan a menudo para automatizar tareas repetitivas. Repetir tareas idénticas o
similares sin cometer errores es algo que las computadoras hacen bien y las personas mal. En un
programa de computadora, la repetición también se llama iteración.
y
Ya hemos visto dos funciones, que iteran usando recursividad. ,
Debido a que la iteración es tan común, Python proporciona funciones de lenguaje para que sea más fácil.
Una es la declaración que vimos en la Sección 4.2. Volveremos a eso más tarde.
Casi se puede leer el comunicado como si fuera inglés. Significa: “Mientras sea mayor que 0,
muestra el valor de y luego disminuye . Cuando llegue a 0, muestre la palabra
”
Más formalmente, aquí está el flujo de ejecución para un declaración:
1. Determinar si la condición es verdadera o falsa.
2. Si es falso, salga de la declaración y continuar la ejecución en la próxima declaración.
3. Si la condición es verdadera, ejecute el cuerpo y luego regrese al paso 1.
Este tipo de flujo se llama bucle porque el tercer paso vuelve a la parte superior.
El cuerpo del ciclo debe cambiar el valor de una o más variables para que la condición se vuelva
falsa eventualmente y el ciclo termine. De lo contrario, el bucle se repetirá para siempre, lo que
se denomina bucle infinito. Una fuente inagotable de diversión para los informáticos es la
observación de que las instrucciones del champú, "Hacer espuma, enjuagar, repetir", son un
bucle infinito.
En el caso de podemos probar
, que el ciclo termina: si es cero o negativo, el ciclo nunca se ejecuta. De lo
contrario, se vuelve más pequeño cada vez que pasa por el bucle, por lo que eventualmente tenemos que
llegar a 0.
Para algunos otros bucles, no es tan fácil saberlo. Por ejemplo:
Cada vez que pasa por el ciclo, el programa genera el valor de y luego verifica si es par o impar.
Si es par, se divide por 2. Si es impar, el valor de se reemplaza por es 3, los valores resultantes
. Por ejemplo, si el argumento pasado es 3, de
10, 5, 16, 8, 4, 2, 1.
Dado que a veces aumenta ya veces disminuye, no hay una prueba obvia de que alguna vez
,
llegue a 1 o de que el programa finalice. Para algunos valores particulares de podemos probar la
terminación. Por ejemplo, si el valor inicial es una potencia de dos, será par cada vez que pase
por el ciclo hasta que llegue a 1. El ejemplo anterior termina con tal secuencia, comenzando con
16.
La pregunta difícil es si podemos probar que este programa termina para todos los valores
positivos de . ¡Hasta ahora, nadie ha podido probarlo o refutarlo! (Ver
.)
Machine Translated by Google
66 Capítulo 7. Iteración
Como ejercicio, reescriba la recursividad de la Sección 5.8 usando iteración en lugar de
de la función.
7.4
A veces no sabes que es hora de terminar un ciclo hasta que llegas a la mitad del cuerpo.
En ese caso, puede usar la declaración para salir del bucle.
Por ejemplo, suponga que desea recibir información del usuario hasta que escriba: . Tú podrías
La condición del bucle es , lo cual siempre es cierto, por lo que el ciclo se ejecuta hasta que llega al descanso
declaración.
Cada vez que pasa, le indica al usuario un paréntesis angular. Si el usuario escribe , la
instrucción sale del bucle. De lo contrario, el programa repite lo que el usuario escriba y vuelve al
principio del bucle. Aquí hay una ejecución de muestra:
Esta forma de escribir bucles es común porque puede verificar la condición en cualquier lugar del bucle
(no solo en la parte superior) y puede expresar la condición de parada afirmativamente ("detenerse
cuando esto suceda") en lugar de negativamente ("continuar hasta que eso suceda ”).
7.5 Raíces cuadradas
Los bucles se usan a menudo en programas que calculan resultados numéricos comenzando con una
respuesta aproximada y mejorándola iterativamente.
Por ejemplo, una forma de calcular raíces cuadradas es el método de Newton. Suponga que quiere saber
la raíz cuadrada de a. Si comienza con casi cualquier estimación, x, puede calcular una mejor estimación
con la siguiente fórmula:
x + a/xy
= 2
Por ejemplo, si a es 4 y x es 3:
Machine Translated by Google
7.6. Algoritmos 67
El resultado está más cerca de la respuesta correcta (√ 4 = 2). Si repetimos el proceso con la nueva estimación,
se acerca aún más:
Después de algunas actualizaciones más, la estimación es casi exacta:
En general, no sabemos de antemano cuántos pasos se necesitan para llegar a la respuesta correcta, pero
sabemos cuándo llegamos allí porque la estimación deja de cambiar:
Cuando podemos
, parar. Aquí hay un ciclo que comienza con una estimación inicial, y la mejora hasta que deja
de cambiar:
Para la mayoría de los valores esto funciona bien, pero en general es peligroso probar la igualdad.
Los valores de punto flotante son solo aproximadamente correctos: la mayoría de los números racionales, como 1/3, y
los números irracionales, como √ 2, no se pueden representar exactamente con un .
En lugar de comprobar si y son exactamente iguales, es más seguro utilizar la función integrada para calcular el
ción valor absoluto, o la magnitud, de la diferencia entre ellos:
7.6 Algoritmos
El método de Newton es un ejemplo de algoritmo: es un proceso mecánico para resolver una categoría de
problemas (en este caso, calcular raíces cuadradas).
Machine Translated by Google
68 Capítulo 7. Iteración
Para entender qué es un algoritmo, puede ser útil comenzar con algo que no sea un algoritmo. Cuando
aprendiste a multiplicar números de un solo dígito, probablemente memorizaste la tabla de multiplicar. En efecto,
memorizaste 100 soluciones específicas. Ese tipo de conocimiento no es algorítmico.
Pero si eras "perezoso", es posible que hayas aprendido algunos trucos. Por ejemplo, para encontrar el producto
de n y 9, puedes escribir n − 1 como el primer dígito y 10 − n como el segundo dígito.
Este truco es una solución general para multiplicar cualquier número de un solo dígito por 9. ¡Eso es un algoritmo!
De manera similar, las técnicas que aprendiste para la suma con acarreo, la resta con préstamo y la división
larga son todos algoritmos. Una de las características de los algoritmos es que no requieren ninguna inteligencia
para llevarse a cabo. Son procesos mecánicos en los que cada paso se sigue del anterior de acuerdo con un
conjunto simple de reglas.
Ejecutar algoritmos es aburrido, pero diseñarlos es interesante, intelectualmente desafiante y una parte central
de las ciencias de la computación.
Algunas de las cosas que la gente hace de forma natural, sin dificultad ni pensamiento consciente, son las más
difíciles de expresar algorítmicamente. Comprender el lenguaje natural es un buen ejemplo.
Todos lo hacemos, pero hasta ahora nadie ha sido capaz de explicar cómo lo hacemos, al menos no en forma
de algoritmo.
7.7 Depuración
A medida que comience a escribir programas más grandes, es posible que dedique más tiempo a la depuración.
Más código significa más posibilidades de cometer un error y más lugares para que los errores se escondan.
Una forma de reducir el tiempo de depuración es "depuración por bisección". Por ejemplo, si hay 100 líneas en
su programa y las verifica una a la vez, tomaría 100 pasos.
En su lugar, trate de dividir el problema por la mitad. Busque en la mitad del programa, o cerca de él, un valor
intermedio que pueda verificar. Agregue una declaración (o algo más que tenga un efecto verificable) y ejecute
el programa.
Si la verificación del punto medio es incorrecta, debe haber un problema en la primera mitad del programa.
Si es correcto, el problema está en la segunda mitad.
Cada vez que realiza una comprobación como esta, reduce a la mitad el número de líneas que tiene que buscar.
Después de seis pasos (que son menos de 100), tendrá una o dos líneas de código, al menos en teoría.
En la práctica, no siempre está claro cuál es el “medio del programa” y no siempre es posible comprobarlo. No
tiene sentido contar líneas y encontrar el punto medio exacto. En su lugar, piense en lugares del programa
donde puede haber errores y lugares donde es fácil marcar. A continuación, elija un lugar donde crea que las
posibilidades de que el error esté antes o después de la comprobación son aproximadamente las mismas.
7.8 Glosario
reasignación: Asignar un nuevo valor a una variable que ya existe.
Machine Translated by Google
7.9. Ejercicios 69
actualizar: Una asignación donde el nuevo valor de la variable depende del anterior.
inicialización: una asignación que da un valor inicial a una variable que se actualizará.
incremento: una actualización que aumenta el valor de una variable (a menudo en uno).
decremento: Una actualización que disminuye el valor de una variable.
iteración: ejecución repetida de un conjunto de declaraciones utilizando una llamada de función recursiva
o un bucle.
bucle infinito: Un bucle en el que la condición de terminación nunca se cumple.
algoritmo: Un proceso general para resolver una categoría de problemas.
7.9 Ejercicios
Ejercicio 7.1. Copie el bucle de la Sección 7.5 y encapsúlelo en una función llamada que toma como
parámetro, elige un valor razonable de y devuelve una estimación de la raíz cuadrada de
.
Para probarlo, escribe una función llamada que imprime una tabla como esta:
La primera columna es un número, a; la segunda columna es la raíz cuadrada de a calculada con ;
la tercera columna es la raíz cuadrada calculada por ; la cuarta columna es el valor absoluto de la diferencia
entre las dos estimaciones.
Ejercicio 7.2. La función incorporada toma una cadena y la evalúa usando el intérprete de Python. Por
ejemplo:
Escriba una función llamada que solicite iterativamente al usuario, tome la entrada resultante y la evalúe
,
usando e imprima el resultado.
Debe continuar hasta que el usuario ingrese , y luego devolver el valor de la última expresión que
evaluado.
Machine Translated by Google
70 Capítulo 7. Iteración
Ejercicio 7.3. El matemático Srinivasa Ramanujan encontró una serie infinita que puede usarse para generar una
aproximación numérica de 1/π:
1 2 √ 2 ∞ (4k)!(1103 + 26390k) ∑
=
π 9801k (k!) 43964k
=0
Escribe una función que usa esta fórmula para calcular y devolver una estimación del bucle
llamada π. Debería usar un para calcular los términos de la suma hasta que el último término sea menor que
(que es la notación de Python para 10−15). Puedes comprobar el resultado comparándolo con .
Solución: .
Machine Translated by Google
Capítulo 8
Instrumentos de cuerda
Las cadenas no son como los números enteros, flotantes y booleanos. Una cadena es una secuencia, lo
que significa que es una colección ordenada de otros valores. En este capítulo, verá cómo acceder a los
caracteres que forman una cadena y aprenderá algunos de los métodos que proporcionan las cadenas.
8.1 Una cadena es una secuencia
Una cadena es una secuencia de caracteres. Puede acceder a los caracteres de uno en uno con el
operador de paréntesis:
La segunda instrucción selecciona el carácter número 1 de y lo asigna a .
La expresión entre paréntesis se llama índice. El índice indica qué carácter de la secuencia desea
(de ahí el nombre).
Pero es posible que no obtenga lo que espera:
Para la mayoría de las personas, la primera es , no . Pero para los informáticos, la
letra del índice es un desplazamiento desde el principio de la cadena y el desplazamiento de la primera letra es cero.
También lo es la letra 0 ("ceroeth") , es la 1ª letra ("oneeth"), y es la 2ª
de la letra ("doseth").
Como índice puede utilizar una expresión que contenga variables y operadores:
Machine Translated by Google
72 Capítulo 8. Cuerdas
Pero el valor del índice tiene que ser un número entero. De lo contrario obtienes:
8.2
es una función integrada que devuelve el número de caracteres en una cadena:
Para obtener la última letra de una cadena, es posible que tenga la tentación de probar algo como esto:
La razón del índice 6. Como comenzamos
es que an o
contar desde
en cero, las seis letras están numeradas del 0 al
hay letra
5. Para obtener el último carácter, debe restar 1 de
:
O puede usar índices negativos, que cuentan hacia atrás desde el final de la cadena. produce la última
letra, produce la penúltima y así sucesivamente. expresión
8.3 Recorrido con un bucle
Una gran cantidad de cálculos implican procesar una cadena de un carácter a la vez. A menudo
comienzan desde el principio, seleccionan cada personaje por turno, le hacen algo y continúan hasta el
final. Este patrón de procesamiento se llama recorrido. Una forma de escribir un recorrido es con un
bucle:
Este bucle atraviesa la cadena y muestra cada letra en una línea por sí misma. La condición del ciclo es
, así que
igual a la longitud de la cadena, la condición es falsa y el cuerpo del ciclo no se ejecuta. El último carácter
cuando
al que se accede es el que tiene el índice, que es el último carácter de la cadena.
,
Como ejercicio, escriba una función que tome una cadena como argumento y muestre las letras al revés,
una por línea.
Otra forma de escribir un recorrido es con un bucle:
Machine Translated by Google
8.4. Rebanadas de cuerda 73
Figura 8.1: Índices de corte.
Cada vez que se recorre el bucle, el siguiente carácter de la cadena se asigna a la variable
. El bucle continúa hasta que no quedan caracteres.
El siguiente ejemplo muestra cómo usar la concatenación (suma de cadenas) y un bucle para generar una serie
abecedaria (es decir, en orden alfabético). En el libro de Robert McCloskey Abran paso a los patitos, los nombres
de los patitos son Jack, Kack, Lack, Mack, Nack, Ouack, Pack y Quack. Este ciclo genera estos nombres en orden:
La salida es:
Por supuesto, eso no es del todo correcto porque "Ouack" y "Quack" están mal escritos. Como ejercicio, modifique
el programa para corregir este error.
8.4 Cortes de cuerda
Un segmento de una cuerda se llama rebanada. Seleccionar un segmento es similar a seleccionar un personaje:
El operador devuelve la parte de la cadena del carácter "nésimo" al carácter "mésimo", incluido el primero pero
excluyendo el último. Este comportamiento es contradictorio, pero podría ayudar imaginar los índices apuntando
entre los caracteres, como en la Figura 8.1.
Si omite el primer índice (antes de los dos puntos), el segmento comienza al principio de la cadena.
Si omite el segundo índice, el segmento va al final de la cadena:
Machine Translated by Google
74 Capítulo 8. Cuerdas
Si el primer índice es mayor o igual que el segundo, el resultado es una cadena vacía, representada por
dos comillas:
Una cadena vacía no contiene caracteres y tiene una longitud de 0, pero aparte de eso, es igual que
cualquier otra cadena.
Continuando con este ejemplo, ¿qué piensas ¿medio? Pruébalo y verás.
8.5 Las cadenas son inmutables
Es tentador usar el operador en el lado izquierdo de una asignación, con la intención de cambiar un carácter
en una cadena. Por ejemplo:
El "objeto" en este caso es la cadena y el "elemento" es el carácter que intentaste asignar.
Por ahora, un objeto es lo mismo que un valor, pero refinaremos esa definición más adelante (Sección 10.10).
El motivo del error es que las cadenas son inmutables, lo que significa que no puede cambiar una cadena
existente. Lo mejor que puede hacer es crear una nueva cadena que sea una variación de la original:
Este ejemplo concatena una nueva primera letra en una porción de . No tiene efecto sobre el
la cadena original.
8.6 Búsqueda
¿Qué hace la siguiente función?
Machine Translated by Google
8.7. Bucle y contando 75
el carácter es el inverso del operador. En lugar de tomar un índice y extraer En cierto sentido,
correspondiente, toma un carácter y encuentra el índice donde aparece ese carácter. Si no se encuentra
el carácter, la función devuelve .
Este es el primer ejemplo que hemos visto de una sentencia dentro de un bucle. Si la
, función sale del ciclo y regresa inmediatamente.
Si el carácter no aparece en la cadena, el programa sale del ciclo normalmente y vuelve
vueltas
Este patrón de cálculo (atravesar una secuencia y volver cuando encontramos lo que buscamos) se
denomina búsqueda .
8.7 Bucle y conteo
El siguiente programa cuenta el número de veces que aparece la letra en una cadena:
Este programa demuestra otro patrón de cálculo llamado contador. La variable se inicializa a 0 y luego
se incrementa cada vez que se encuentra an. Cuando el bucle sale, contiene el resultado: el
número total de 's.
Como ejercicio, encapsule este código en una función denominada , y generalizarlo de modo que
que acepte la cadena y la letra como argumentos.
Luego, reescriba la función para que, en lugar de recorrer la cadena, use la versión de tres parámetros
de la sección anterior.
8.8 Métodos de cadena
Las cadenas proporcionan métodos que realizan una variedad de operaciones útiles. Un método es
similar a una función, toma argumentos y devuelve un valor, pero la sintaxis es diferente. Por ejemplo,
el método toma una cadena y devuelve una nueva cadena con todas las letras mayúsculas.
En lugar de la sintaxis de la función , utiliza la sintaxis del método .
Machine Translated by Google
76 Capítulo 8. Cuerdas
Esta forma de notación de puntos especifica el nombre del método, la , y el nombre del. Los
cadena a la que aplicar el método, paréntesis vacíos indican que este método
no toma argumentos.
Una llamada de método se denomina invocación; en este caso, diríamos que estamos invocando
en .
Resulta que hay un método de cadena llamado función que que es notablemente similar a la
escribimos:
En este ejemplo, invocamos en y pasar la letra que buscamos como param
éter
En realidad, el método es más general que nuestra función; puede encontrar subcadenas, no solo
los personajes:
De forma comienza al principio de la cadena, pero puede tomar un segundo argumento, el
predeterminada, indexe donde debería comenzar:
Este es un ejemplo de un argumento opcional; también puede tomar un tercer argumento, el índice
donde debe parar:
Esta búsqueda falla porque no aparece en el rango de índice desde hasta sin incluir . ,
La búsqueda hasta el segundo índice, pero sin incluirlo, es coherente con el operador de división.
8.9 El operador
La palabra es un operador booleano que toma dos cadenas y devuelve peras como si la primera ap
una subcadena en la segunda:
Por ejemplo, la siguiente función imprime todas las letras de que también aparecen en
:
Machine Translated by Google
8.10. Comparación de cadenas 77
Con nombres de variables bien elegidos, Python a veces se lee como inglés. Podría leer este ciclo, "para (cada)
letra en (la primera) palabra, si (la) letra (aparece) en (la segunda) palabra, imprima (la) letra".
Esto es lo que obtienes si comparas manzanas y naranjas:
8.10 Comparación de cadenas
Los operadores relacionales trabajan en cadenas. Para ver si dos cadenas son iguales:
Otras operaciones relacionales son útiles para poner palabras en orden alfabético:
Python no maneja las letras mayúsculas y minúsculas de la misma manera que la gente. Todas las letras
mayúsculas vienen antes que todas las letras minúsculas, así que:
Una forma común de abordar este problema es convertir cadenas a un formato estándar, como minúsculas, antes
de realizar la comparación. Tenlo en cuenta en caso de que tengas que defenderte de un hombre armado con
una piña.
8.11 Depuración
Cuando usa índices para recorrer los valores en una secuencia, es complicado obtener el comienzo y el final
correctos del recorrido. Aquí hay una función que se supone que compara dos palabras y regresa
si una de las palabras es el reverso de la otra, pero contiene dos errores:
Machine Translated by Google
78 Capítulo 8. Cuerdas
La primera instrucción comprueba si las palabras tienen la misma longitud. Si no, podemos regresar
inmediatamente. De lo contrario, para el resto de la función, podemos suponer que las palabras
son de la misma longitud. Este es un ejemplo del patrón de tutor en la Sección 6.8.
y son índices: las poligonales adelante mientras atraviesa hacia atrás. Si
encuentran dos letras que no coinciden, podemos nosotros inmediatamente. Si pasamos por el
.
devolver el bucle completo y todas las letras coinciden, volvemos
Si probamos esta función con las palabras "pots" y "stop", esperamos el valor de retorno ,
pero obtenemos un IndexError:
Para depurar este tipo de error, mi primer movimiento es imprimir los valores de los índices
inmediatamente antes de la línea donde aparece el error.
Ahora, cuando vuelvo a ejecutar el programa, obtengo más información:
La primera vez que pasa por el bucle, el valor de es 4, que está fuera del rango de la cadena. El
índice del último carácter es 3, por lo que el valor inicial de debe ser
.
Si soluciono ese error y vuelvo a ejecutar el programa, obtengo:
Esta vez obtenemos la respuesta correcta, pero parece que el ciclo solo se ejecutó tres veces, lo cual es
sospechoso. Para tener una mejor idea de lo que está sucediendo, es útil dibujar un diagrama de estado.
Durante la primera iteración, el marco para se muestra en la Figura 8.2.
Me tomé una licencia al ordenar las variables en el cuadro y agregar líneas de puntos para mostrar
que los valores de e indican los caracteres en y .
Comenzando con este diagrama, ejecute el programa en papel, cambiando los valores de y
durante cada iteración. Encuentre y corrija el segundo error en esta función.
Machine Translated by Google
8.12. Glosario 79
i 0 j 3
Figura 8.2: Diagrama de estado.
8.12 Glosario
objeto: Algo a lo que una variable puede referirse. Por ahora, puede usar "objeto" y "valor" indistintamente.
secuencia: Una colección ordenada de valores donde cada valor es identificado por un número entero
índice.
item: Uno de los valores en una secuencia.
índice: un valor entero utilizado para seleccionar un elemento en una secuencia, como un carácter en una cadena.
En Python, los índices comienzan desde 0.
rebanada: una parte de una cadena especificada por un rango de índices.
cadena vacía: una cadena sin caracteres y de longitud 0, representada por dos comillas
marcas.
inmutable: la propiedad de una secuencia cuyos elementos no se pueden cambiar.
atravesar: iterar a través de los elementos en una secuencia, realizando una operación similar en
cada.
búsqueda: Un patrón de recorrido que se detiene cuando encuentra lo que está buscando.
contador: una variable utilizada para contar algo, generalmente inicializada a cero y luego incrementada
mentalizado
invocación: Una declaración que llama a un método.
argumento opcional: un argumento de función o método que no es obligatorio.
8.13 Ejercicios
Ejercicio 8.1. Lea la documentación de los métodos de cadena en . Es
posible que desee experimentar con algunos de ellos
para asegurarse de que entiende cómo funcionan. y que son particularmente útiles.
La documentación utiliza una sintaxis que puede resultar confusa. Por ejemplo, entre
, paréntesis indique argumentos opcionales. Entonces es obligatorio, pero
,
es opcional, y si lo incluye, entonces es opcional.
Ejercicio 8.2. Hay un método de cadena llamado que es similar a la función de la Sección 8.7.
Lea la documentación de este método y escriba una invocación que cuente el número de 's en
.
Ejercicio 8.3. Un segmento de cadena puede tomar un tercer índice que especifica el "tamaño de paso"; es decir, el
número de espacios entre caracteres sucesivos. Un tamaño de paso de 2 significa cualquier otro carácter; 3 significa
cada tercio, etc.
Machine Translated by Google
80 Capítulo 8. Cuerdas
Un tamaño de paso de 1 recorre la palabra hacia atrás, por lo que el segmento genera una cadena invertida.
Usa este modismo para escribir una versión de una línea del Ejercicio 6.3.
Ejercicio 8.4. Todas las siguientes funciones están destinadas a verificar si una cadena contiene letras minúsculas, pero al
menos algunas de ellas son incorrectas. Para cada función, describa lo que realmente hace la función (asumiendo que el
parámetro es una cadena).
Ejercicio 8.5. Un cifrado César es una forma débil de encriptación que implica "rotar" cada letra en un número fijo de lugares.
Rotar una letra significa cambiarla por el alfabeto, volviendo al principio si es necesario, de modo que 'A' rotada por 3 es 'D' y
'Z' rotada por 1 es 'A'.
Para rotar una palabra, rota cada letra en la misma cantidad. Por ejemplo, "cheer" rotado por 7 es "jolly" y "melon" rotado por
10 es "cubed". En la película 2001: A Space Odyssey, la computadora de la nave se llama HAL, que es IBM rotada por 1.
Escriba una función llamada que tome una cadena y un entero como parámetros y devuelva una nueva cadena que contenga
las letras de la cadena original rotadas por la cantidad dada.
Es posible que desee utilizar la función integrada , que convierte un carácter en un código numérico, y
Machine Translated by Google
8.13. Ejercicios 81
, que convierte códigos numéricos en caracteres. Las letras del alfabeto se codifican en orden
alfabético, por ejemplo:
Porque es la segunda letra del alfabeto. Pero cuidado: los códigos numéricos de las letras mayúsculas
son diferentes.
Los chistes potencialmente ofensivos en Internet a veces se codifican en ROT13, que es un cifrado
César con rotación 13. Si no se ofende fácilmente, encuentre y decodifique algunos de ellos. Solución:
.
Machine Translated by Google
82 Capítulo 8. Cuerdas
Machine Translated by Google
Capítulo 9
Estudio de caso: juego de palabras
Este capítulo presenta el segundo caso de estudio, que consiste en resolver acertijos de palabras
buscando palabras que tengan ciertas propiedades. Por ejemplo, encontraremos los palíndromos más
largos en inglés y buscaremos palabras cuyas letras aparezcan en orden alfabético. Y presentaré otro
plan de desarrollo del programa: la reducción a un problema previamente resuelto.
9.1 Lectura de listas de palabras
Para los ejercicios de este capítulo necesitamos una lista de palabras en inglés. Hay muchas listas de
palabras disponibles en la Web, pero la más adecuada para nuestro propósito es una de las listas de
palabras recopiladas y aportadas al dominio público por Grady Ward como parte del proyecto Moby lexi
con (ver ). Es una lista de 113.809 crucigramas oficiales; es decir, palabras que se consideran válidas
en crucigramas y otros juegos de palabras. En la colección Moby, el nombre del archivo es ; puede
descargar una copia, con el nombre más simple de
, .
Este archivo está en texto sin formato, por lo que puede abrirlo con un editor de texto, pero también puede leerlo
desde Python. La función incorporada toma el nombre del archivo como parámetro y devuelve un objeto de
archivo que puede usar para leer el archivo.
es un nombre común para un objeto de archivo utilizado para la entrada. El objeto de archivo proporciona
métodos para leer, incluso en una , varios que leen los caracteres del archivo hasta que obtiene
nueva línea y devuelve el resultado como una cadena:
La primera palabra en esta lista en particular es “aa”, que es una especie de lava. La secuencia
representa el carácter de nueva línea que separa esta palabra de la siguiente.
El objeto de archivo realiza un seguimiento de dónde se encuentra en el archivo, por lo que si llama de nuevo, obtienes
a la siguiente palabra:
Machine Translated by Google
84 Capítulo 9. Estudio de caso: juego de palabras
La siguiente palabra es "aah", que es una palabra perfectamente legítima, así que deja de mirarme así. O, si lo
que le molesta es el carácter de nueva línea, podemos deshacernos de él con el método de cadena
:
También puede usar un objeto de archivo como parte de bucle. Este programa lee y
una impresión de cada palabra, una por línea:
9.2 Ejercicios
Hay soluciones a estos ejercicios en la siguiente sección. Al menos debe intentar cada uno antes de leer las
soluciones.
Ejercicio 9.1. Escriba un programa que lea e imprime solo las palabras con más de 20
caracteres (sin contar los espacios en blanco).
Ejercicio 9.2. En 1939 Ernest Vincent Wright publicó una novela de 50.000 palabras llamada Gadsby que no
contiene la letra "e". Dado que la "e" es la letra más común en inglés, no es fácil de hacer.
De hecho, es difícil construir un pensamiento solitario sin usar ese símbolo tan común. Es lento al principio, pero
con precaución y horas de entrenamiento se puede ir ganando facilidad.
Está bien, me detendré ahora.
Escriba un programa que lea e imprime solo las palabras que no tienen "e". Calcular el
el porcentaje de palabras en la lista que no tienen "e".
Ejercicio 9.3. Escriba una función llamada que tome una palabra y una cadena de letras prohibidas, y que
regrese si la palabra no usa ninguna de las letras prohibidas.
Escriba un programa que solicite al usuario que ingrese una cadena de letras prohibidas y luego imprima la
cantidad de palabras que no contienen ninguna de ellas. ¿Puedes encontrar una combinación de 5 letras
prohibidas que excluya la menor cantidad de palabras?
Ejercicio 9.4. Escriba una función llamada que tome una palabra y una cadena de letras, y que regrese si la
palabra contiene solo letras en la lista. ¿Puedes hacer una oración usando solo las letras? Aparte de "Hoe
alfalfa"?
Ejercicio 9.5. Escriba una función llamada que tome una palabra y una cadena de letras requeridas, y que
regrese si la palabra usa todas las letras requeridas al menos una vez. ¿Cuántas palabras hay que usan todas
las vocales? Qué tal si ?
Ejercicio 9.6. Escriba una función llamada que devuelva si las letras de una palabra aparecen en orden alfabético
(las letras dobles están bien). ¿Cuántas palabras abecedarias hay?
Machine Translated by Google
9.3. Buscar 85
9.3 Buscar
Todos los ejercicios de la sección anterior tienen algo en común; se pueden resolver con el patrón de
búsqueda que vimos en la Sección 8.6. El ejemplo más simple es:
El bucle atraviesa los caracteres en . Si encontramos la letra “e”, podemos inmediatamente;
devolver de lo contrario, tenemos que ir a la siguiente letra. Si salimos del ciclo normalmente, eso significa
que no encontramos una "e", por lo que regresamos .
Podría escribir esta función de manera más concisa usando el operador, pero comencé con esta versión
porque demuestra la lógica del patrón de búsqueda.
es una versión más general de pero tiene la misma estructura:
podemos volver tan pronto como encontremos una carta prohibida; si llegamos al final del bucle,
regresamos .
es similar excepto que el sentido de la condición se invierte:
En lugar de una lista de letras prohibidas, tenemos una lista de letras disponibles. Si encontramos una letra en
eso no esta en , podemos volver .
es similar excepto que invertimos el papel de la palabra y la cadena de letras:
En lugar de recorrer las letras en las , el bucle atraviesa las letras requeridas. Si alguno de los
letras requeridas que no aparecen en la palabra, podemos regresar .
Si realmente estuvieras pensando como un científico informático, habrías reconocido que era una instancia
de un problema previamente resuelto y habrías escrito:
Este es un ejemplo de un plan de desarrollo de programa llamado reducción a un problema previamente
resuelto, lo que significa que usted reconoce el problema en el que está trabajando como una instancia de
un problema resuelto y aplica una solución existente.
Machine Translated by Google
86 Capítulo 9. Estudio de caso: juego de palabras
9.4 Bucles con índices
Escribí las funciones en la sección anterior con caracteres en bucles porque solo necesitaba el
las cadenas; No tuve que hacer nada con los índices.
En tenemos que comparar letras adyacentes, lo cual es un poco complicado con un
bucle:
Una alternativa es usar la recursividad:
Otra opción es utilizar un bucle:
El ciclo comienza en y termina cuando . Cada vez que pasa por el bucle, compara el iésimo carácter (que
puede considerar como el carácter actual) con el i + 1° carácter (que puede considerar como el siguiente).
Si el siguiente carácter es menor que (alfabéticamente anterior) al actual, entonces hemos descubierto una
ruptura en la tendencia abecedaria y volvemos .
Si llegamos al final del ciclo sin encontrar una falla, entonces la palabra pasa la prueba. Para convencerse de
que el ciclo termina correctamente, considere un ejemplo como . La longitud de la palabra es 6, por lo que la
última vez que se ejecuta el ciclo es cuando es 4, que es el índice del penúltimo carácter. En la última iteración,
compara el penúltimo carácter con el último, que es lo que queremos.
Aquí hay una versión del (ver Ejercicio 6.3) que utiliza dos índices; uno comienza en
principio y sube; el otro comienza al final y va hacia abajo.
Machine Translated by Google
9.5. depuración 87
O podríamos reducir a un problema previamente resuelto y escribir:
Usando de la Sección 8.11.
9.5 Depuración
Probar programas es difícil. Las funciones de este capítulo son relativamente fáciles de probar porque puede comprobar
los resultados a mano. Aun así, es entre difícil e imposible elegir un conjunto de palabras que prueben todos los
posibles errores.
Tomando como ejemplo, hay dos casos obvios para verificar: las palabras que tienen una 'e' deben regresar y las
palabras que no deben regresar.
, No deberías tener problemas para encontrar uno de cada uno.
Dentro de cada caso, hay algunos subcasos menos obvios. Entre las palabras que tienen una "e", debe probar palabras
con una "e" al principio, al final y en algún lugar en el medio. Debe probar palabras largas, palabras cortas y palabras
muy cortas, como la cadena vacía. La cadena vacía es un ejemplo de un caso especial, que es uno de los casos no
obvios donde a menudo acechan los errores.
Además de los casos de prueba que genera, también puede probar su programa con una lista de palabras como . Al
escanear la salida, es posible que pueda detectar errores, pero tenga cuidado: puede detectar un tipo de error (palabras
que no deben incluirse, pero lo están) y no otro (palabras que deben incluirse, pero no lo están) .
En general, las pruebas pueden ayudarlo a encontrar errores, pero no es fácil generar un buen conjunto de casos de
prueba, e incluso si lo hace, no puede estar seguro de que su programa sea correcto. Según un legendario informático:
Las pruebas de programas se pueden usar para mostrar la presencia de errores, ¡pero nunca para
mostrar su ausencia!
—Edsger W. Dijkstra
9.6 Glosario
objeto de archivo: un valor que representa un archivo abierto.
reducción a un problema previamente resuelto: una forma de resolver un problema expresándolo
como una instancia de un problema previamente resuelto.
caso especial: un caso de prueba que es atípico o no obvio (y es menos probable que se maneje de forma incorrecta).
correctamente).
Machine Translated by Google
88 Capítulo 9. Estudio de caso: juego de palabras
9.7 Ejercicios
Ejercicio 9.7. Esta pregunta se basa en un Puzzler que se transmitió en el programa de radio Car Talk (
):
Dame una palabra con tres letras dobles consecutivas. Te daré un par de palabras que casi califican,
pero no lo hacen. Por ejemplo, la palabra comité, comité. Estaría genial salvo por la 'i' que se cuela
por ahí. O Mississippi: Mississi ppi. Si pudieras eliminar esos i's, funcionaría. Pero hay una palabra
que tiene tres pares consecutivos de letras y que yo sepa, esta puede ser la única palabra.
Por supuesto, probablemente haya 500 más, pero solo puedo pensar en uno. ¿Que es la palabra?
Escribe un programa para encontrarlo. .
Solución: Ejercicio 9.8. Aquí hay otro Car Talk Puzzler ( ):
“Estaba conduciendo por la carretera el otro día y me fijé en mi cuentakilómetros.
Como la mayoría de los odómetros, muestra seis dígitos, solo en millas enteras. Entonces, si mi auto tuviera
300,000 millas, por ejemplo, vería 300000.
“Ahora, lo que vi ese día fue muy interesante. Noté que los últimos 4 dígitos eran palindrómicos; es
decir, se leen igual hacia adelante que hacia atrás. Por ejemplo, 5445 es un palíndromo, por lo que
mi cuentakilómetros podría haber indicado 315445.
“Una milla después, los últimos 5 números eran palindrómicos. Por ejemplo, podría haber leído
365456. Una milla después de eso, los 4 de los 6 números del medio eran palindrómicos. ¿Y estás
listo para esto? ¡Una milla después, los 6 eran palindrómicos!
“La pregunta es, ¿qué había en el odómetro cuando miré por primera vez?”
Escriba un programa en Python que pruebe todos los números de seis dígitos e imprima cualquier número que
satisfaga estos requisitos. .
Solución: Ejercicio 9.9. Aquí hay otro Car Talk Puzzler que puedes resolver con una búsqueda (
):
“Recientemente tuve una visita con mi mamá y nos dimos cuenta de que los dos dígitos que forman
mi edad cuando se invierten dan como resultado su edad. Por ejemplo, si ella tiene 73 años, yo tengo
37. Nos preguntamos con qué frecuencia ha sucedido esto a lo largo de los años, pero nos distrajimos
con otros temas y nunca encontramos una respuesta.
“Cuando llegué a casa me di cuenta de que los dígitos de nuestras edades han sido reversibles seis
veces hasta ahora. También me di cuenta de que, si teníamos suerte, volvería a suceder en unos
años, y si realmente teníamos suerte, sucedería una vez más después de eso. En otras palabras,
habría sucedido 8 veces en total. Entonces la pregunta es, ¿cuántos años tengo ahora?”
Escriba un programa en Python que busque soluciones para este rompecabezas. Sugerencia: es posible que encuentre el método
de cadena útil.
Solución: .
Machine Translated by Google
Capítulo 10
Liza
Este capítulo presenta uno de los tipos integrados más útiles de Python, las listas. También aprenderá más sobre los
objetos y lo que puede suceder cuando tiene más de un nombre para el mismo objeto.
10.1 Una lista es una secuencia
Al igual que una cadena, una lista es una secuencia de valores. En una cadena, los valores son caracteres; en una
lista, pueden ser de cualquier tipo. Los valores de una lista se denominan elementos o, a veces, elementos.
Hay varias formas de crear una nueva lista; la más sencilla es encerrar los elementos entre corchetes ( y ):
El primer ejemplo es una lista de cuatro enteros. El segundo es una lista de tres cadenas. Los elementos de una lista
no tienen que ser del mismo tipo. La siguiente lista contiene una cadena, un flotante, un entero y (¡vaya!) otra lista:
Una lista dentro de otra lista está anidada.
Una lista que no contiene elementos se denomina lista vacía; puede crear uno con corchetes vacíos, .
Como era de esperar, puede asignar valores de lista a las variables:
Machine Translated by Google
90 Capítulo 10. Listas
lista
quesos 0 'Queso Cheddar'
1 'Queso Edam'
2 'Gouda'
lista
números 0 42
1 123
5
lista
vacío
Figura 10.1: Diagrama de estado.
10.2 Las listas son mutables
La sintaxis para acceder a los elementos de una lista es la misma que para acceder a los
caracteres de una cadena: el operador de paréntesis. La expresión entre corchetes especifica el índice.
Recuerda que los índices empiezan en 0:
A diferencia de las cadenas, las listas son mutables. Cuando el operador de paréntesis aparece en el
lado izquierdo de una asignación, identifica el elemento de la lista que se asignará.
El elemento unoésimo de , que antes era 123, ahora es 5.
La figura 10.1 muestra el diagrama de estado , y .
para las listas están representadas por cuadros con la palabra "lista" afuera y los elementos de la lista
se refieren a una lista con tres elementos indexados 0, 1 y 2. contiene
adentro. dos elementos; el diagrama muestra que el valor del segundo elemento ha sido
reasignado de 123 a 5. se refiere a una lista sin elementos.
Los índices de lista funcionan de la misma manera que los índices de cadena:
• Cualquier expresión entera se puede utilizar como índice.
• Si intenta leer o escribir un elemento que no existe, obtiene un • Si un .
índice tiene un valor negativo, cuenta hacia atrás desde el final de la lista.
El operador también trabaja en listas.
Machine Translated by Google
10.3. Atravesando una lista 91
10.3 Recorriendo una lista
La forma más común de recorrer los elementos de una lista es con lo bucle. la sintaxis es
mismo que para las cadenas:
Esto funciona bien si solo necesita leer los elementos de la lista. Pero si desea escribir o actualizar
los elementos, necesita los índices. Una forma común de hacerlo es combinar las funciones
integradas y :
Este bucle atraviesa la lista y actualiza cada elemento. devuelve el número de elementos de la
lista. devuelve una lista de índices de 0 a n − 1, donde n es la longitud de la lista.
Cada vez que pasa por el ciclo obtiene el índice del siguiente elemento. La declaración de
asignación en el cuerpo se usa para leer el valor antiguo del elemento y para asignar el nuevo valor.
Un bucle sobre una lista vacía nunca ejecuta el cuerpo:
Aunque una lista puede contener otra lista, la lista anidada sigue contando como un solo elemento. La
longitud de esta lista es de cuatro:
10.4 Operaciones de lista
El operador concatena listas:
El operador repite una lista un número determinado de veces:
El primer ejemplo se repite cuatro veces. El segundo ejemplo repite la lista.
tres veces.
10.5 Sectores de lista
El operador de corte también funciona en listas:
Machine Translated by Google
92 Capítulo 10. Listas
Si omite el primer índice, el segmento comienza al principio. Si omites el segundo, el corte va hasta el final.
Entonces, si omite ambos, el segmento es una copia de la lista completa.
Dado que las listas son mutables, a menudo es útil hacer una copia antes de realizar operaciones que
modifiquen las listas.
Un operador de división en el lado izquierdo de una tarea puede actualizar varios elementos:
10.6 Métodos de lista
Python proporciona métodos que operan en listas. Por ejemplo, al final de agrega un nuevo elemento
una lista:
toma una lista como argumento y agrega todos los elementos:
Este ejemplo no se modifica.
ordena los elementos de la lista de menor a mayor:
La mayoría de los métodos de lista son nulos; modifican la lista y . Si accidentalmente escribes
regresan, te decepcionará el resultado.
Machine Translated by Google
10.7. Mapear, filtrar y reducir 93
10.7 Mapear, filtrar y reducir
Para sumar todos los números en una lista, puede usar un ciclo como este:
se inicializa en 0. Cada vez que pasa por el ciclo, obtiene un elemento de la lista.
El operador proporciona una forma breve de actualizar una variable. Esta tarea aumentada
declaración,
es equivalente a
A medida que se ejecuta el bucle, acumula la suma de los elementos; una variable utilizada de esta manera a
veces se denomina acumulador.
Sumar los elementos de una lista es una operación tan común que Python la proporciona como una función
integrada, :
Una operación como esta que combina una secuencia de elementos en un solo valor a veces se denomina
reducir.
A veces desea recorrer una lista mientras crea otra. Por ejemplo, la siguiente función toma una lista de
cadenas y devuelve una nueva lista que contiene cadenas en mayúsculas:
se inicializa con una lista vacía; cada vez que recorremos el bucle, añadimos el siguiente elemento.
Entonces es otro tipo de acumulador.
Una operación como a veces se denomina mapa porque "mapea" una función ) en cada
(en este caso el método uno de los elementos de una secuencia.
Otra operación común es seleccionar algunos de los elementos de una lista y devolver una sublista.
Por ejemplo, la siguiente función toma una lista de cadenas y devuelve una lista que contiene solo las cadenas
en mayúsculas:
Machine Translated by Google
94 Capítulo 10. Listas
es un método de cadena que devuelve si la cadena contiene solo letras mayúsculas.
Una operación como se llama filtro porque selecciona algunos de los elementos y
filtra las demás.
Las operaciones de lista más comunes se pueden expresar como una combinación de mapa, filtro y reducción.
10.8 Eliminación de elementos
Hay varias formas de eliminar elementos de una lista. Si conoce el índice del elemento que desea, puede usar:
modifica la lista y devuelve el elemento que se eliminó. Si no proporciona un
index, borra y devuelve el último elemento.
Si no necesita el valor eliminado, puede usar el operador:
Si conoce el elemento que desea eliminar (pero no el índice), puede usar :
El valor de retorno de es .
Para eliminar más de un elemento, puede utilizar con un índice de corte:
Como de costumbre, el segmento selecciona todos los elementos hasta el segundo índice, pero sin incluirlo.
10.9 Listas y cadenas
Una cadena es una secuencia de caracteres y una lista es una secuencia de valores, pero una lista de caracteres
no es lo mismo que una cadena. Para convertir de una cadena a una lista de caracteres, puede usar :
Machine Translated by Google
10.10. Objetos y valores 95
a 'banana' a
'banana'
b 'banana' b
Figura 10.2: Diagrama de estado.
Porque es el nombre de una función incorporada, debe evitar usarla como una variable
nombre. También lo evito porque se parece demasiado a . Entonces es por eso que uso .
La función divide una cadena en letras individuales. Si desea dividir una cadena en palabras, puede usar el
método:
Un argumento opcional llamado delimitador especifica qué caracteres usar como aries vinculados a palabras.
El siguiente ejemplo utiliza un guión como delimitador:
es el inverso de . Toma una lista de cadenas y concatena los elementos. es un método de
cadena, por lo que debe invocarlo en el delimitador y pasar la lista como parámetro:
En este caso, el delimitador es un carácter de espacio, por lo que pone un espacio entre palabras. Para
concatenar cadenas sin espacios, puede usar la cadena vacía, , como delimitador.
10.10 Objetos y valores
Si ejecutamos estas sentencias de asignación:
Lo sabemos y ambos se refieren a una cadena, pero no sabemos si se refieren a la misma cadena. Hay dos
estados posibles, que se muestran en la figura 10.2.
En un caso, y se refieren a dos objetos diferentes que tienen el mismo valor. En el segundo caso, se refieren al
mismo objeto.
Para verificar si dos variables se refieren al mismo objeto, puede usar el operador.
Machine Translated by Google
96 Capítulo 10. Listas
a [1, 2, 3]
b [1, 2, 3]
Figura 10.3: Diagrama de estado.
a
[ 1, 2, 3 ]
b
Figura 10.4: Diagrama de estado.
En este ejemplo, Python solo creó un objeto de cadena y ambos se refieren a él. Pero cuando creas dos listas,
obtienes dos objetos:
Entonces, el diagrama de estado se parece a la figura 10.3.
En este caso diríamos que las dos listas son equivalentes, porque tienen los mismos elementos, pero no
idénticas, porque no son el mismo objeto. Si dos objetos son idénticos, también son equivalentes, pero si son
equivalentes, no necesariamente son idénticos.
Hasta ahora, hemos estado usando "objeto" y "valor" indistintamente, pero es más preciso decir que un objeto
tiene un valor. Si evalúas, obtienes un objeto de lista cuyo valor es una secuencia de enteros. Si otra lista tiene
los mismos elementos, decimos que tiene el mismo valor, pero no es el mismo objeto.
10.11 Alias
Si se refiere a un objeto y le asignas , entonces ambas variables se refieren al mismo objeto:
El diagrama de estado se parece a la figura 10.4.
La asociación de una variable con un objeto se denomina referencia. En este ejemplo, hay dos referencias al
mismo objeto.
Un objeto con más de una referencia tiene más de un nombre, por lo que decimos que el objeto tiene un alias.
Si el objeto con alias es mutable, los cambios realizados con un alias afectan al otro:
Machine Translated by Google
10.12. Listar argumentos 97
lista
__principal__ letras
0 'a'
1 'b'
eliminar_cabeza t
2 'C'
Figura 10.5: Diagrama de pila.
Aunque este comportamiento puede ser útil, es propenso a errores. En general, es más seguro evitar la
creación de alias cuando se trabaja con objetos mutables.
Para objetos inmutables como cadenas, el alias no es un gran problema. En este ejemplo:
Casi nunca hace una diferencia si y se refieren a la misma cadena o no.
10.12 Argumentos de lista
Cuando pasa una lista a una función, la función obtiene una referencia a la lista. Si la función modifica
la lista, la persona que llama ve el cambio. Por ejemplo, elimina el primer elemento de una lista:
Así es como se usa:
El parámetro y la variable se parece son alias para el mismo objeto. El diagrama de pila
a la figura 10.5.
Como la lista es compartida por dos marcos, la dibujé entre ellos.
Es importante distinguir entre operaciones que modifican listas y operaciones que crean nuevas listas.
Por ejemplo, el método modifica una lista, pero el operador crea una nueva lista.
Aquí hay un ejemplo usando :
Machine Translated by Google
98 Capítulo 10. Listas
El valor de retorno de es .
Aquí hay un ejemplo usando el operador:
El resultado del operador es una nueva lista y la lista original no cambia.
Esta diferencia es importante cuando escribe funciones que se supone que modifican listas.
Por ejemplo, esta función no elimina el encabezado de una lista:
El operador de división crea una nueva lista y la asignación hace referencia a ella, pero eso no afecta a la
persona que llama.
Al comienzo de una , y consulte la misma lista. Al final se refiere
nueva lista, pero aún se refiere a la lista original sin modificar.
Una alternativa es escribir una función que cree y devuelva una nueva lista. Por ejemplo, devuelve todos
menos el primer elemento de una lista:
Esta función deja la lista original sin modificar. Así es como se usa:
10.13 Depuración
El uso descuidado de listas (y otros objetos mutables) puede llevar a largas horas de depuración. Aquí hay
algunas trampas comunes y formas de evitarlas:
1. La mayoría de los métodos de lista modifican el argumento y devuelven . Esto es lo contrario de los
métodos de cadena, que devuelven una nueva cadena y dejan el original en paz.
Si está acostumbrado a escribir código de cadena como este:
Es tentador escribir un código de lista como este:
Machine Translated by Google
10.13. depuración 99
Antes de usar métodos y operadores de lista, debe leer la documentación detenidamente y
luego probarlos en modo interactivo.
2. Elija un idioma y quédese con él.
Parte del problema con las listas es que hay demasiadas formas de hacer las cosas. Por ejemplo,
, asignación
para eliminar un elemento de una lista, puede utilizar o incluso una , d,e sector.
Para agregar un elemento, puede usar es método o el operador. Asumiendo que
una lista y es un elemento de lista, estos son correctos:
Y estos están mal:
Pruebe cada uno de estos ejemplos en modo interactivo para asegurarse de que entiende lo que
hacen. Tenga en cuenta que solo el último provoca un error de tiempo de ejecución; los otros
tres son legales, pero hacen lo incorrecto.
3. Haga copias para evitar la creación de alias.
Si desea utilizar un método como ese que modifica el argumento, pero también necesita
conservar la lista original, puede hacer una copia.
En este ejemplo, también podría usar la lista ordenada de , que devuelve un nuevo,
funciones incorporada y dejar el original solo.
Machine Translated by Google
100 Capítulo 10. Listas
10.14 Lista del glosario: Una
secuencia de valores.
elemento: Uno de los valores en una lista (u otra secuencia), también llamados elementos.
lista anidada: Una lista que es un elemento de otra lista.
acumulador: una variable utilizada en un bucle para sumar o acumular un resultado.
asignación aumentada: una declaración que actualiza el valor de una variable usando una ópera
como .
reducir: Un patrón de procesamiento que atraviesa una secuencia y acumula los elementos en
un solo resultado.
mapa: Un patrón de procesamiento que atraviesa una secuencia y realiza una operación en cada
elemento.
filtro: Un patrón de procesamiento que recorre una lista y selecciona los elementos que satisfacen
criterio.
objeto: Algo a lo que una variable puede referirse. Un objeto tiene un tipo y un valor.
equivalente: Que tiene el mismo valor.
idéntico: Ser el mismo objeto (lo que implica equivalencia).
referencia: La asociación entre una variable y su valor.
aliasing: una circunstancia en la que dos o más variables se refieren al mismo objeto.
delimitador: un carácter o cadena que se utiliza para indicar dónde debe dividirse una cadena.
10.15 Ejercicios
Puedes descargar soluciones a estos ejercicios desde
.
Ejercicio 10.1. Escriba una función llamada que tome una lista de listas de números enteros y sume los
elementos de todas las listas anidadas. Por ejemplo:
Ejercicio 10.2. Escriba una función llamada que tome una lista de números y devuelva la suma acumulativa; es
decir, una nueva lista donde el iésimo elemento es la suma de los primeros i + 1 elementos de la lista original.
Por ejemplo:
Ejercicio 10.3. Escriba una función llamada que tome una lista y devuelva una nueva lista que contenga todos los
elementos menos el primero y el último. Por ejemplo:
Machine Translated by Google
10.15. Ejercicios 101
Ejercicio 10.4. Escriba una función llamada que tome una lista, la modifique eliminando el primer y el último elemento y
devuelva . Por ejemplo:
Ejercicio 10.5. Escriba una función llamada si que toma una lista como parámetro y devuelve lo
la lista está ordenada en orden ascendente y contrario. Por ejemplo:
Ejercicio 10.6. Dos palabras son anagramas si puedes reorganizar las letras de una para deletrear la otra.
Escriba una función llamada que tome dos cadenas y devuelva si son anagramas.
Ejercicio 10.7. Escriba una función llamada que tome una lista y regrese si hay algún elemento que aparece más de una
vez. No debe modificar la lista original.
Ejercicio 10.8. Este ejercicio pertenece a la llamada Paradoja del Cumpleaños, sobre la cual puedes leer
en .
Si hay 23 estudiantes en tu clase, ¿cuál es la probabilidad de que dos de ellos tengan el mismo cumpleaños?
Puede estimar esta probabilidad generando muestras aleatorias de 23 cumpleaños y buscando coincidencias. Sugerencia:
puede generar cumpleaños aleatorios con la función en el módulo.
Puede descargar mi solución del Ejercicio .
10.9. Escriba una función que lea el archivo por palabra. y construye una lista con un método de
Escribe dos versiones de esta función, una usando el modismo elemento y el otro usando
. ¿Cuál tarda más en correr? ¿Por qué?
Solución: .
Ejercicio 10.10. Para verificar si una palabra está en la lista de palabras, puede usar sería lento operador, pero
porque busca las palabras en orden.
Debido a que las palabras están en orden alfabético, podemos acelerar las cosas con una búsqueda de bisección
(también conocida como búsqueda binaria), que es similar a lo que hace cuando busca una palabra en el diccionario (el
libro, no la estructura de datos) . Comienza en el medio y verifica si la palabra que está buscando viene antes de la
palabra en el medio de la lista. Si es así, busque la primera mitad de la lista de la misma manera. De lo contrario, busca
la segunda mitad.
De cualquier manera, cortas el espacio de búsqueda restante a la mitad. Si la lista de palabras tiene 113.809 palabras,
se necesitarán unos 17 pasos para encontrar la palabra o concluir que no está allí.
Escribe una función llamada que toma una lista ordenada y un valor objetivo y devuelve si no si
la palabra está en la lista y lo es.
O puede leer la documentación del ¡Módulo y utilízalo! Solución:
.
Ejercicio 10.11. Dos palabras son un "par inverso" si cada una es el reverso de la otra. Escriba un programa que
encuentre todos los pares inversos en la lista de palabras. Solución:
.
Ejercicio 10.12. Dos palabras se “entrelazan” si al tomar letras alternas de cada una se forma una nueva palabra. Por
ejemplo, "zapato" y "frío" se entrelazan para formar "escolarizado". Solución:
Machine Translated by Google
102 Capítulo 10. Listas
. Crédito: Este ejercicio está inspirado en un ejemplo en
.
1. Escriba un programa que encuentre todos los pares de palabras que se entrelazan. Pista: ¡no enumeres todos los pares!
2. ¿Puedes encontrar alguna palabra que esté entrelazada en tres direcciones? es decir, cada tercera letra forma
una palabra, a partir de la primera, segunda o tercera?
Machine Translated by Google
Capítulo 11
Diccionarios
Este capítulo presenta otro tipo incorporado llamado diccionario. Los diccionarios son una de las mejores
características de Python; son los componentes básicos de muchos algoritmos eficientes y elegantes.
11.1 Un diccionario es un mapeo
Un diccionario es como una lista, pero más general. En una lista, los índices deben ser números enteros;
en un diccionario pueden ser (casi) cualquier tipo.
Un diccionario contiene una colección de índices, que se denominan claves, y una colección de valores.
Cada clave está asociada con un único valor. La asociación de una clave y un valor se denomina par clave
valor o, a veces, elemento.
En lenguaje matemático, un diccionario representa una asignación de claves a valores, por lo que también
puede decir que cada clave "se asigna" a un valor. Como ejemplo, construiremos un diccionario que mapea
palabras de inglés a español, por lo que las claves y los valores son todas cadenas.
La función crea un nuevo diccionario sin elementos. Debido a que es una función es el nombre de un
incorporada, debe evitar usarla como nombre de variable.
Los corchetes ondulados representan un diccionario vacío. Para agregar elementos al diccionario, puede
usar corchetes:
Esta línea crea un elemento que se asigna desde la clave hasta el valor . Si volvemos a
imprimir el diccionario, vemos un par clavevalor con dos puntos entre la clave y el valor:
Este formato de salida es también un formato de entrada. Por ejemplo, puede crear un nuevo diccionario
con tres elementos:
Machine Translated by Google
104 Capítulo 11. Diccionarios
Pero si imprimes , te sorprenderías:
El orden de los pares clavevalor puede no ser el mismo. Si escribe el mismo ejemplo en su
computadora, es posible que obtenga un resultado diferente. En general, el orden de los elementos en
un diccionario es impredecible.
Pero eso no es un problema porque los elementos de un diccionario nunca se indexan con índices
enteros. En su lugar, utiliza las teclas para buscar los valores correspondientes:
Si la clave no está en el diccionario, obtendrá una excepción:
La función funciona en diccionarios; devuelve el número de pares clavevalor:
El operador también trabaja con diccionarios; te dice si algo aparece como una clave en el diccionario
(aparecer como un valor no es lo suficientemente bueno).
Para ver si algo aparece como valor en un diccionario, puedes usar el método
, que devuelve una colección de valores, y luego usa el operador:
El operador utiliza diferentes algoritmos para listas y diccionarios. Para las listas, busca los elementos de la
lista en orden, como en la Sección 8.6. A medida que la lista se hace más larga, el tiempo de búsqueda se
hace más largo en proporción directa.
Los diccionarios de Python usan una estructura de datos llamada tabla hash que tiene una propiedad
notable: el operador tarda aproximadamente la misma cantidad de tiempo sin importar cuántos elementos
haya en el diccionario. Explico cómo es eso posible en la Sección B.4, pero es posible que la explicación
no tenga sentido hasta que haya leído algunos capítulos más.
11.2 Diccionario como colección de fichas
Suponga que le dan una cadena y desea contar cuántas veces aparece cada letra.
Hay varias formas de hacerlo:
Machine Translated by Google
11.2. Diccionario como colección de contadores 105
1. Podrías crear 26 variables, una para cada letra del alfabeto. Luego podría recorrer la cadena
y, para cada carácter, incrementar el contador correspondiente, probablemente usando un
condicional encadenado.
2. Podrías crear una lista con 26 elementos. Luego, podría convertir cada carácter en un número
(usando la función integrada), usar el número como un índice en la lista e incrementar el
contador apropiado.
3. Podrías crear un diccionario con caracteres como claves y contadores como los valores
correspondientes. La primera vez que vea un carácter, agregaría un elemento al diccionario.
Después de eso, incrementaría el valor de un elemento existente.
Cada una de estas opciones realiza el mismo cálculo, pero cada una de ellas implementa ese
cálculo de una manera diferente.
Una implementación es una forma de realizar un cálculo; algunas implementaciones son mejores
que otras. Por ejemplo, una ventaja de la implementación del diccionario es que no tenemos que
saber de antemano qué letras aparecen en la cadena y solo tenemos que dejar espacio para las
letras que aparecen.
Así es como se vería el código:
El nombre de la función es (o , que es un término estadístico para una colección de contadores
frecuencias).
La primera línea de la función crea un diccionario vacío. El lazo atraviesa la cuerda.
Cada vez que recorremos el bucle, si el carácter no está en el diccionario, creamos un nuevo
elemento con clave y el valor inicial 1 (ya que hemos visto esta letra una vez). Si ya está en el
diccionario incrementamos .
Así es como funciona:
Los diccionarios tienen un método que toma una clave y un valor predeterminado. Si la
llamado aparece en el clave devuelve el valor correspondiente; de lo contrario, devuelve el
diccionario, valor predeterminado. Por ejemplo:
Machine Translated by Google
106 Capítulo 11. Diccionarios
11.3 Bucles y diccionarios
Si usa un diccionario en una declaración, atraviesa las claves del diccionario. Por ejemplo, imprime
cada clave y el valor correspondiente:
Así es como se ve la salida:
Una vez más, las claves no están en ningún orden en particular. Para recorrer las teclas en orden ordenado, puede
usar la función incorporada :
11.4 Búsqueda inversa
Dado un diccionario y una clave, es fácil encontrar el valor correspondiente. La . Este
operación se denomina búsqueda .
Pero, ¿y si lo tienes y lo quieres encontrar? Tiene dos problemas: primero, puede haber más de una
clave que se asigna al valor. Dependiendo de la aplicación, es posible que pueda elegir una, o puede
que tenga que hacer una lista que las contenga todas. En segundo lugar, no existe una sintaxis simple
para realizar una búsqueda inversa; tienes que buscar
Aquí hay una función que toma un valor y devuelve la primera clave que se asigna a ese valor:
Machine Translated by Google
11.5. diccionarios y listas 107
Esta función es otro ejemplo más del patrón de búsqueda, pero utiliza una función que no hemos
visto antes, . La declaración de aumento provoca una excepción; en este caso, provoca una
, excepción integrada que se usa para indicar que una operación de búsqueda falló.
Si llegamos al final del bucle, eso significa que no aparece en el diccionario como un valor, por lo que
generamos una excepción.
Aquí hay un ejemplo de una búsqueda inversa exitosa:
Y uno fallido:
El efecto cuando genera una excepción es el mismo que cuando Python genera una: imprime un
seguimiento y un mensaje de error.
Cuando genera una excepción, puede proporcionar un mensaje de error detallado como argumento
opcional. Por ejemplo:
Una búsqueda inversa es mucho más lenta que una búsqueda directa; si tiene que hacerlo a menudo,
o si el diccionario crece, el rendimiento de su programa se verá afectado.
11.5 Diccionarios y listas
Las listas pueden aparecer como valores en un diccionario. Por ejemplo, si le dan un diccionario
que asigna letras a frecuencias, es posible que desee invertirlo; es decir, cree un diccionario que
mapee desde frecuencias hasta letras. Dado que puede haber varias letras con la misma
frecuencia, cada valor en el diccionario invertido debe ser una lista de letras.
Aquí hay una función que invierte un diccionario:
Machine Translated by Google
108 Capítulo 11. Diccionarios
'pag' 1 1 'pag'
'r' 2 2 't'
't' 1 3 'o'
'o' 1
lista
2 0 'r'
Figura 11.1: Diagrama de estado.
Cada vez que pasa por el bucle, obtiene una clave de y obtiene el valor correspondiente.
ignifica que no lo h, emos visto antes, creamos un nuevo elemento y no lo inicializamos con
Si eso sun
singleton (una lista que contiene un solo elemento). De lo contrario, hemos visto este valor antes,
por lo que agregamos la clave correspondiente a la lista.
Aquí hay un ejemplo:
. se representa como un cuadro
y Un diccionario
La figura 11.1 es un diagrama de estado que muestra
con el tipo encima y los pares clavevalor dentro. Si los valores son enteros, flotantes o cadenas, los
dibujo dentro del cuadro, pero generalmente dibujo listas fuera del cuadro, solo para simplificar el
diagrama.
Las listas pueden ser valores en un diccionario, como muestra este ejemplo, pero no pueden ser claves. Esto
es lo que sucede si lo intentas:
Mencioné anteriormente que un diccionario se implementa usando una tabla hash y eso significa que
las claves tienen que ser hashable.
Un hash es una función que toma un valor (de cualquier tipo) y devuelve un número entero. Los diccionarios
usan estos números enteros, llamados valores hash, para almacenar y buscar pares clavevalor.
Este sistema funciona bien si las claves son inmutables. Pero si las claves son mutables, como las
listas, suceden cosas malas. Por ejemplo, cuando crea un par clavevalor, Python procesa la clave
y la almacena en la ubicación correspondiente. Si modifica la clave y luego la vuelve a codificar, iría
a una ubicación diferente. En ese caso, es posible que tenga dos entradas para la misma clave o
que no pueda encontrar una clave. De cualquier manera, el diccionario no funcionaría correctamente.
Es por eso que las claves tienen que ser hashable y por qué los tipos mutables como las listas no lo son.
La forma más sencilla de sortear esta limitación es utilizar tuplas, que veremos en el próximo capítulo.
Machine Translated by Google
11.6. Notas 109
fibonacci
norte 4
fibonacci fibonacci
norte 3 norte 2
fibonacci fibonacci
norte 1 norte
0
Figura 11.2: Gráfico de llamadas.
Dado que los diccionarios son mutables, no se pueden usar como claves, pero se pueden usar como valores.
11.6 Notas
Si jugó con la función de la Sección 6.7, es posible que haya notado que cuanto mayor sea el argumento
que proporcione, más tiempo tardará en ejecutarse la función. Además, el tiempo de ejecución aumenta
rápidamente.
Para entender por qué, considere la figura 11.2, que muestra el gráfico de llamadas para con
:
Un gráfico de llamadas muestra un conjunto de marcos de función, con líneas que conectan cada marco con los
con con y .
marcos de las funciones a las que llama. En la parte superior del gráfico, llamadas
A su vez, con con y . Etcétera. llamadas
Cuente cuántas veces y son llamados. Este es un ineficiente
soluciona el problema, y empeora a medida que la discusión se hace más grande.
Una solución es realizar un seguimiento de los valores que ya se han calculado almacenándolos en un
diccionario. Un valor previamente calculado que se almacena para su uso posterior se denomina memo.
Aquí hay una versión "memorizada" de :
es un diccionario que realiza un seguimiento de los números de Fibonacci que ya conocemos. Comienza
con dos elementos: 0 se asigna a 0 y 1 se asigna a 1.
Machine Translated by Google
110 Capítulo 11. Diccionarios
Cada vez que se llama, comprueba si el resultado ya .está allí, puede volver inmediatamente. De lo
contrario, debe calcular el nuevo valor, agregarlo al diccionario y devolverlo.
Si ejecuta esta versión de es y compárelo con el original, encontrará que
mucho más rápido.
11.7 Variables globales
En el ejemplo anterior, se crea fuera de la función, por lo que pertenece al marco especial llamado . Las
variables en a veces se denominan globales porque se puede acceder a ellas desde cualquier función.
A diferencia de las variables locales, que desaparecen cuando finaliza su función, las variables globales
persisten de una llamada de función a la siguiente.
Es común usar variables globales para banderas; es decir, variables booleanas que indican ("bandera")
si una condición es verdadera. Por ejemplo, algunos programas usan un indicador a
llamado control del nivel de detalle en la salida:
Si intenta reasignar una variable global, puede que se sorprenda. Se supone que el siguiente ejemplo
realiza un seguimiento de si se ha llamado a la función:
Pero si lo ejecuta verá que el valor de no cambia El problema es ese .
La variable local desaparece
crea una ncueva variable
uando local
finaliza llamada
la función
y no tiene ningún efecto sobre la variable
global.
Para reasignar una variable global dentro de una función, debe declarar la variable global antes de
usarla:
La declaración global le dice al intérprete algo como, “En esta función, cuando digo me refiero a la
, variable global; no crees uno local”.
Aquí hay un ejemplo que intenta actualizar una variable global:
Si lo ejecutas obtienes:
Machine Translated by Google
11.8. depuración 111
Python asume que es local y, bajo esa suposición, lo está leyendo antes de escribirlo. La solución, de nuevo,
es declarar global.
Si una variable global hace referencia a un valor mutable, puede modificar el valor sin declarar la variable:
Entonces puede agregar, eliminar y reemplazar elementos de una lista global o diccionario, pero si desea
reasignar la variable, debe declararla:
Las variables globales pueden ser útiles, pero si tiene muchas y las modifica con frecuencia, pueden dificultar
la depuración de programas.
11.8 Depuración
A medida que trabaja con conjuntos de datos más grandes, puede volverse difícil depurar imprimiendo y
verificando la salida a mano. Estas son algunas sugerencias para depurar grandes conjuntos de datos:
Reducir la entrada: si es posible, reduzca el tamaño del conjunto de datos. Por ejemplo, si el programa lee
un archivo de texto, comience solo con las primeras 10 líneas o con el ejemplo más pequeño que
pueda encontrar. Puede editar los archivos o (mejor) modificar el programa para que lea solo las
primeras líneas.
Si hay un error, puede reducirlo al valor más pequeño que manifieste el error y luego aumentarlo
gradualmente a medida que encuentre y corrija los errores.
Verifique resúmenes y tipos: en lugar de imprimir y verificar todo el conjunto de datos, considere imprimir
resúmenes de los datos: por ejemplo, la cantidad de elementos en un diccionario o el total de una lista
de números.
Una causa común de errores de tiempo de ejecución es un valor que no es del tipo correcto. Para
depurar este tipo de error, suele ser suficiente imprimir el tipo de un valor.
Escribir autocomprobaciones: a veces puede escribir código para comprobar si hay errores automáticamente.
Por ejemplo, si está calculando el promedio de una lista de números, puede verificar que el resultado
no sea mayor que el elemento más grande de la lista ni menor que el más pequeño.
Esto se denomina "control de cordura" porque detecta resultados que son "locos".
Otro tipo de verificación compara los resultados de dos cálculos diferentes para ver si son consistentes.
Esto se llama una "verificación de consistencia".
Machine Translated by Google
112 Capítulo 11. Diccionarios
Formatee la salida: Formatear la salida de depuración puede facilitar la detección de un error. Vimos un ejemplo en
la Sección 6.9. Otra herramienta que puede resultarle útil es el módulo, que proporciona una función que
muestra los tipos incorporados en un formato más legible para los humanos (significa “letra bonita”).
Una vez más, el tiempo que dedica a construir andamios puede reducir el tiempo que dedica a la depuración.
11.9 Glosario
Mapeo: Una relación en la que cada elemento de un conjunto corresponde a un elemento de
otro conjunto
diccionario: Un mapeo de claves a sus valores correspondientes.
par clavevalor: La representación del mapeo de una clave a un valor.
elemento: en un diccionario, otro nombre para un par clavevalor.
clave: un objeto que aparece en un diccionario como la primera parte de un par clavevalor.
valor: un objeto que aparece en un diccionario como la segunda parte de un par clavevalor. Esto es
más específico que nuestro uso anterior de la palabra "valor".
implementación: Una forma de realizar un cálculo.
hashtable: el algoritmo utilizado para implementar los diccionarios de Python.
función hash: una función utilizada por una tabla hash para calcular la ubicación de una clave.
hashable: un tipo que tiene una función hash. Los tipos inmutables como enteros, flotantes y cadenas son hashable;
los tipos mutables como listas y diccionarios no lo son.
búsqueda: una operación de diccionario que toma una clave y encuentra el valor correspondiente.
búsqueda inversa: Una operación de diccionario que toma un valor y encuentra una o más claves que
mapearlo.
declaración de aumento: una declaración que (deliberadamente) genera una excepción.
singleton: Una lista (u otra secuencia) con un solo elemento.
gráfico de llamada: un diagrama que muestra cada marco creado durante la ejecución de un programa,
con una flecha de cada llamador a cada destinatario.
memo: Un valor calculado almacenado para evitar cálculos futuros innecesarios.
variable global: una variable definida fuera de una función. Se puede acceder a las variables globales
de cualquier función.
declaración global: una declaración que declara un nombre de variable global.
bandera: una variable booleana utilizada para indicar si una condición es verdadera.
declaración: Una declaración como que le dice al intérprete algo sobre una variable.
Machine Translated by Google
11.10. Ejercicios 113
11.10 Ejercicios
Ejercicio 11.1. Escriba una función que lea las palabras y las almacene como claves en un diccionario. No importa
cuáles sean los valores. Luego puede usar el operador como una forma rápida de verificar si una cadena está en el
diccionario.
Si hizo el ejercicio 10.10, puede comparar la velocidad de esta implementación con el operador de lista y la búsqueda
de bisección.
Ejercicio 11.2. Lea la documentación del método de diccionario versión más y úsalo para escribir un
concisa de . Solución:
.
Ejercicio 11.3. Memorice la función de Ackermann del ejercicio 6.2 y vea si la memorización hace posible evaluar la
función con argumentos más grandes. Pista: no. Solución:
.
Ejercicio 11.4. Si hiciste el ejercicio 10.7, ya tienes una función llamada que toma una lista como parámetro y
regresa si hay algún objeto que aparece más de una vez en la lista.
Use un diccionario para escribir una versión más rápida y simple de . Solución:
.
Ejercicio 11.5. Dos palabras son "rotar pares" si puedes rotar una de ellas y obtener la otra (ver
en el ejercicio 8.5).
Escriba un programa que lea una lista de palabras y encuentre todos los pares rotativos. Solución:
.
Ejercicio 11.6. Aquí hay otro Puzzler de Car Talk (
):
Esto fue enviado por un tipo llamado Dan O'Leary. Recientemente se encontró con una palabra
común de una sílaba y cinco letras que tiene la siguiente propiedad única. Cuando quitas la primera
letra, las letras restantes forman un homófono de la palabra original, es decir, una palabra que suena
exactamente igual. Reemplace la primera letra, es decir, vuelva a colocarla y elimine la segunda letra
y el resultado es otro homófono de la palabra original. Y la pregunta es, ¿cuál es la palabra?
Ahora les voy a dar un ejemplo que no funciona. Veamos la palabra de cinco letras, 'wrack'. WRACK,
sabes que te gusta 'retorcer con dolor'. Si elimino la primera letra, me queda una palabra de cuatro
letras, 'RACK'. Como en, '¡Santa vaca, viste el estante en ese dólar! ¡Debe haber sido un nueve
puntos! Es un homófono perfecto. Si vuelves a poner la 'w' y quitas la 'r', te queda la palabra 'wack',
que es una palabra real, pero no es un homófono de las otras dos palabras.
Pero hay, sin embargo, al menos una palabra que Dan y nosotros conocemos, que producirá dos
homófonos si elimina cualquiera de las dos primeras letras para formar dos nuevas palabras de cuatro
letras. La pregunta es, ¿cuál es la palabra?
Puede usar el diccionario del ejercicio 11.1 para verificar si una cadena está en la lista de palabras.
Para verificar si dos palabras son homófonas, puede usar el diccionario de pronunciación CMU. Puedes descargarlo
desde o de
y también puede descargar que
, proporciona una función llamada que lee el
diccionario de pronunciación y devuelve un diccionario de Python que asigna cada palabra a una cadena que
describe su pronunciación principal.
Machine Translated by Google
114 Capítulo 11. Diccionarios
Escriba un programa que enumere todas las palabras que resuelven el rompecabezas. Solución:
.
Machine Translated by Google
Capítulo 12
tuplas
Este capítulo presenta un tipo incorporado más, la tupla, y luego muestra cómo las listas, los diccionarios y las
tuplas funcionan juntos. También presento una función útil para las listas de argumentos de longitud variable, los
operadores de recopilación y dispersión.
Una nota: no hay consenso sobre cómo pronunciar "tupla". Algunas personas dicen "tuh ple", que rima con
"supple". Pero en el contexto de la programación, la mayoría de la gente dice "demasiado", que rima con "cuádruple".
12.1 Las tuplas son inmutables
Una tupla es una secuencia de valores. Los valores pueden ser de cualquier tipo y están indexados por números
enteros, por lo que, en ese sentido, las tuplas se parecen mucho a las listas. La diferencia importante es que las tuplas
son inmutables.
Sintácticamente, una tupla es una lista de valores separados por comas:
Aunque no es necesario, es común encerrar las tuplas entre paréntesis:
Para crear una tupla con un solo elemento, debe incluir una coma final:
Un valor entre paréntesis no es una tupla:
Otra forma de crear una tupla es la función integrada una tupla vacía: . Sin argumento, crea
Machine Translated by Google
116 Capítulo 12. Tuplas
Si el argumento es una secuencia (cadena, lista o tupla), el resultado es una tupla con los elementos de
la secuencia:
Porque es el nombre de una función incorporada, debe evitar usarla como una variable
nombre.
La mayoría de los operadores de listas también funcionan con tuplas. El operador de paréntesis indexa un elemento:
Y el operador de corte selecciona un rango de elementos.
Pero si intenta modificar uno de los elementos de la tupla, obtiene un error:
Debido a que las tuplas son inmutables, no puede modificar los elementos. Pero puedes reemplazar una
tupla por otra:
Esta declaración crea una nueva tupla y luego hace referencia a ella.
Los operadores relacionales trabajan con tuplas y otras secuencias; Python comienza comparando el
primer elemento de cada secuencia. Si son iguales, pasa a los siguientes elementos, y así sucesivamente,
hasta encontrar elementos que difieren. Los elementos posteriores no se consideran (incluso si son
realmente grandes).
12.2 Asignación de tuplas
A menudo es útil intercambiar los valores de dos variables. Con las asignaciones convencionales, debe
utilizar una variable temporal. Por ejemplo, para intercambiar y:
Esta solución es engorrosa; la asignación de tuplas es más elegante:
Machine Translated by Google
12.3. Tuplas como valores de retorno 117
El lado izquierdo es una tupla de variables; el lado derecho es una tupla de expresiones. Cada valor se asigna a
su respectiva variable. Todas las expresiones del lado derecho se evalúan antes que cualquiera de las
asignaciones.
El número de variables a la izquierda y el número de valores a la derecha tiene que ser el
mismo:
Más generalmente, el lado derecho puede ser cualquier tipo de secuencia (cadena, lista o tupla). Por ejemplo,
para dividir una dirección de correo electrónico en un nombre de usuario y un dominio, podría escribir:
El valor de retorno del es una lista con dos elementos; el primer elemento se asigna a
, segundo al .
12.3 Tuplas como valores de retorno
Estrictamente hablando, una función solo puede devolver un valor, pero si el valor es una tupla, el efecto es el
mismo que devolver múltiples valores. Por ejemplo, si desea dividir dos números enteros y calcular el cociente y
el resto, no es eficiente calcular y luego . Es mejor calcular ambos al mismo tiempo.
La función incorporada toma dos argumentos y devuelve una tupla de dos valores, el cociente y el resto. Puede
almacenar el resultado como una tupla:
O use la asignación de tuplas para almacenar los elementos por separado:
Aquí hay un ejemplo de una función que devuelve una tupla:
y son funciones integradas que encuentran los elementos más grandes y más pequeños de una secuencia.
calcula ambos y devuelve una tupla de dos valores.
Machine Translated by Google
118 Capítulo 12. Tuplas
12.4 Tuplas de argumento de longitud variable
Las funciones pueden tomar un número variable de argumentos. Un nombre de parámetro que comienza con toma
reúne argumentos en una tupla. Por ejemplo, y los imprime: cualquier número de argumentos
El parámetro de recopilación puede tener el nombre que desee, pero la es convencional. Así es cómo
función funciona:
El complemento de reunir es dispersar. Si tiene una secuencia de valores y desea pasarla a una función como múltiples
argumentos, puede usar el operador. Por ejemplo, toma exactamente dos argumentos; no funciona con una
tupla:
Pero si dispersas la tupla, funciona:
Muchas de las funciones integradas utilizan tuplas de argumentos de longitud variable. Por ejemplo, y puede tomar
cualquier número de argumentos:
Pero no es.
Como ejercicio, escribe una función llamada que toma cualquier número de argumentos y
devuelve su suma.
12.5 Listas y tuplas
es una función integrada que toma dos o más secuencias y las intercala. El nombre de la función hace referencia
a una cremallera, que intercala dos filas de dientes.
Este ejemplo comprime una cadena y una lista:
El resultado es un objeto zip que sabe cómo iterar a través de los pares. El uso más común de bucle:
está en un
Machine Translated by Google
12.5. Listas y tuplas 119
Un objeto zip es un tipo de iterador, que es cualquier objeto que itera a través de una secuencia.
Los iteradores son similares a las listas en algunos aspectos, pero a diferencia de las listas, no puede usar un índice para
seleccionar un elemento de un iterador.
Si desea usar operadores y métodos de lista, puede usar un objeto zip para hacer una lista:
El resultado es una lista de tuplas; en este ejemplo, cada tupla contiene un carácter de la cadena y el
elemento correspondiente de la lista.
Si las secuencias no tienen la misma longitud, el resultado tiene la longitud de la más corta.
Puede utilizar la asignación de tuplas en un bucle para recorrer una lista de tuplas:
Cada vez que pasa por el bucle, Python selecciona la siguiente tupla de la lista y le asigna el elemento
mentos a y . La salida de este bucle es:
Si combina y tupla la a,signación, obtiene un idioma útil para atravesar dos (o más) secuencias al
mismo tiempo. Por ejemplo, and y devuelve si hay un índice toma dos secuencias,
tal que , :
Si necesita recorrer los elementos de una secuencia y sus índices, puede usar la función incorporada
:
El resultado de es un objeto de enumeración, que itera una secuencia de pares; cada par contiene un
índice (a partir de 0) y un elemento de la secuencia dada. En este ejemplo, la salida es
De nuevo.
Machine Translated by Google
120 Capítulo 12. Tuplas
12.6 Diccionarios y tuplas
Los diccionarios tienen un método llamado que devuelve una secuencia de tuplas, donde cada
tupla que es un par clavevalor.
El resultado es objeto, que es un iterador que itera los pares clavevalor. Haces un
una lata usarlo en un bucle así:
Como cabría esperar de un diccionario, los elementos no están en ningún orden en particular.
Yendo en la otra dirección, puede usar una lista de tuplas para inicializar un nuevo diccionario:
El método del diccionario también toma una lista de tuplas y las agrega, como pares clavevalor,
a un diccionario existente.
Es común usar tuplas como claves en los diccionarios (principalmente porque no puede usar
listas). Por ejemplo, un directorio telefónico podría asignar pares de apellido y nombre a números
de teléfono. Suponiendo que hemos definido podríamos , y
escribir: ,
La expresión entre paréntesis es una tupla. Podríamos usar la asignación de tuplas para recorrer este
diccionario.
Este ciclo atraviesa las claves en , que son tuplas. Asigna los elementos de cada uno
tupla hacia y , y luego imprime el nombre y número de teléfono correspondiente.
Hay dos formas de representar tuplas en un diagrama de estado. La versión más detallada
muestra los índices y elementos tal como aparecen en una lista. Por ejemplo, la tupla aparecería
como en la Figura 12.1.
Pero en un diagrama más grande, es posible que desee omitir los detalles. Por ejemplo, un diagrama
de la guía telefónica podría aparecer como en la figura 12.2.
Aquí las tuplas se muestran usando la sintaxis de Python como una abreviatura gráfica. El número
de teléfono en el diagrama es la línea de quejas de la BBC, así que por favor no llame.
Machine Translated by Google
12.7. Secuencias de secuencias 121
tupla
0 'Clese'
1 'John'
Figura 12.1: Diagrama de estado.
dictar
('Clese', 'Juan') '08700 100 222'
('Chapman', 'Graham') '08700 100 222'
('Inactivo', 'Eric') '08700 100 222'
('Gilliam', 'Terry') '08700 100 222'
('Jones', 'Terry') '08700 100 222'
('Palin', 'Michael') '08700 100 222'
Figura 12.2: Diagrama de estado.
12.7 Secuencias de secuencias
Me he centrado en las listas de tuplas, pero casi todos los ejemplos de este capítulo también funcionan
con listas de listas, tuplas de tuplas y tuplas de listas. Para evitar enumerar las posibles combinaciones,
a veces es más fácil hablar de secuencias de secuencias.
En muchos contextos, los diferentes tipos de secuencias (cadenas, listas y tuplas) se pueden usar
indistintamente. Entonces, ¿cómo debería elegir uno sobre los demás?
Para empezar con lo obvio, las cadenas están más limitadas que otras secuencias porque los
elementos tienen que ser caracteres. También son inmutables. Si necesita la capacidad de cambiar
los caracteres en una cadena (en lugar de crear una nueva cadena), es posible que desee utilizar una
lista de caracteres en su lugar.
Las listas son más comunes que las tuplas, principalmente porque son mutables. Pero hay algunos
casos en los que podría preferir tuplas:
1. En algunos contextos, declaración, es sintácticamente más simple crear una tupla
como una lista.
2. Si desea utilizar una secuencia como clave de diccionario, debe utilizar un tipo inmutable
como una tupla o cadena.
3. Si está pasando una secuencia como argumento a una función, el uso de tuplas reduce el
posibilidad de un comportamiento inesperado debido a la formación de alias.
,
Debido a que las tuplas son inmutables, no proporcionan métodos como y que modifican las listas
,
existentes. Pero Python proporciona la función integrada que toma cualquier secuencia
y devuelve
una nueva lista con los mismos elementos ordenados, y que toma una secuencia y ,
devuelve un iterador que recorre la lista en orden inverso.
Machine Translated by Google
122 Capítulo 12. Tuplas
12.8 Depuración
Listas, diccionarios y tuplas son ejemplos de estructuras de datos; en este capítulo estamos empezando a ver
estructuras de datos compuestas, como listas de tuplas o diccionarios que contienen tuplas como claves y listas como
valores. Las estructuras de datos compuestas son útiles, pero son propensas a lo que yo llamo errores de forma; es
decir, errores causados cuando una estructura de datos tiene el tipo, tamaño o estructura incorrectos. Por ejemplo, si
espera una lista con un número entero y le doy un número entero simple (no en una lista), no funcionará.
Para ayudar a depurar este tipo de errores, he escrito un módulo llamado que proporciona una función, también
llamado que toma cualquier tipo de estructura de datos como ,argumento y devuelve una cadena que resume su
forma. Puedes descargarlo desde
Aquí está el resultado de una lista simple:
Un programa más elegante podría escribir "lista de 3 enteros", pero era más fácil no tratar con plurales.
Aquí hay una lista de listas:
Si los elementos de la lista no son del mismo tipo, escriba: los agrupa, en orden, por
Aquí hay una lista de tuplas:
Y aquí hay un diccionario con 3 elementos que asignan números enteros a cadenas.
Si tiene problemas para realizar un seguimiento de sus estructuras de datos, poder ayudar.
12.9 Glosario
tupla: Una secuencia inmutable de elementos.
asignación de tupla: Una asignación con una secuencia en el lado derecho y una tupla de variables en el izquierdo.
El lado derecho se evalúa y luego sus elementos se asignan a las variables de la izquierda.
Machine Translated by Google
12.10. Ejercicios 123
reunir: una operación que recopila múltiples argumentos en una tupla.
dispersión: Una operación que hace que una secuencia se comporte como múltiples argumentos.
objeto zip: El resultado de llamar a una secuencia de función integrada ; un objeto que itera a través de un
de tuplas.
iterador: un objeto que puede iterar a través de una secuencia, pero que no proporciona métodos ni operadores de lista.
estructura de datos: una colección de valores relacionados, a menudo organizados en listas, diccionarios, tuplas,
etc.
error de forma: un error causado porque un valor tiene la forma incorrecta; es decir, el tipo equivocado
o tamaño.
12.10 Ejercicios
Ejercicio 12.1. Escriba una función llamada que tome una cadena e imprima las letras en orden decreciente de frecuencia.
Encuentre muestras de texto de varios idiomas diferentes y vea cómo la frecuencia de las letras varía entre los idiomas.
Compare sus resultados con las tablas en . Solución:
Ejercicio 12.2. ¡Más anagramas!
1. Escriba un programa que lea una lista de palabras de un archivo (vea la Sección 9.1) e imprima todos los conjuntos de
palabras que son anagramas.
Aquí hay un ejemplo de cómo se vería la salida:
Sugerencia: es posible que desee crear un diccionario que mapee desde una colección de letras a una lista de
palabras que se pueden deletrear con esas letras. La pregunta es, ¿cómo puedes representar la colección de letras
de una manera que pueda usarse como clave?
2. Modifique el programa anterior para que imprima primero la lista de anagramas más larga, luego la segunda más
larga y así sucesivamente.
3. En Scrabble, un "bingo" es cuando juegas las siete fichas en tu estante, junto con una letra en el tablero, para formar
una palabra de ocho letras. ¿Qué colección de 8 letras forma la mayor cantidad posible de bingos?
Solución: .
Ejercicio 12.3. Dos palabras forman un “par de metátesis” si puedes transformar una en la otra intercambiando dos letras;
por ejemplo, "conversar" y "conservar". Escriba un programa que encuentre todos los pares de metátesis en el diccionario.
Sugerencia: no pruebe todos los pares de palabras y no pruebe todos los intercambios posibles. Solución: . Crédito: Este
ejercicio está inspirado en un ejemplo en
.
Machine Translated by Google
124 Capítulo 12. Tuplas
Ejercicio 12.4. Aquí hay otro Car Talk Puzzler ( ):
¿Cuál es la palabra inglesa más larga, que sigue siendo una palabra inglesa válida, ya que eliminas
sus letras una a la vez?
Ahora, las letras se pueden quitar de cualquier extremo o del medio, pero no se puede reorganizar
ninguna de las letras. Cada vez que sueltas una letra, terminas con otra palabra en inglés. Si haces
eso, eventualmente terminarás con una letra y esa también será una palabra en inglés, una que se
encuentra en el diccionario. quiero saber cual es la palabra mas larga y cuantas letras tiene
Te voy a dar un pequeño ejemplo modesto: Sprite. ¿De acuerdo? Empiezas con sprite, le quitas una
letra, una del interior de la palabra, le quitas la r, y nos queda la palabra despecho, luego le quitamos
la e al final, nos queda escupir , le quitamos la s, nos queda pit, it, y yo.
Escriba un programa para encontrar todas las palabras que se pueden reducir de esta manera y luego encuentre la más larga.
Este ejercicio es un poco más desafiante que la mayoría, así que aquí hay algunas sugerencias:
1. Es posible que desee escribir una función que tome una palabra y calcule una lista de todas las palabras que
se puede formar quitando una letra. Estos son los “hijos” de la palabra.
2. Recursivamente, una palabra es reducible si alguno de sus hijos es reducible. Como caso base, puede
considere la cadena vacía reducible.
, palabras de una sola letra. Por lo tanto, es posible que desee
3. La lista de palabras que proporcioné no contiene
agregar "I", "a" y la cadena vacía.
4. Para mejorar el rendimiento de su programa, es posible que desee memorizar las palabras que se sabe que
son reducibles.
Solución: .
Machine Translated by Google
Capítulo 13
Estudio de caso: selección de
estructura de datos
En este punto, ha aprendido acerca de las estructuras de datos centrales de Python y ha visto algunos de
los algoritmos que las usan. Si desea saber más sobre algoritmos, este podría ser un buen momento para
leer el Capítulo B. Pero no es necesario que lo lea antes de continuar; Puedes leerlo siempre que estés
interesado.
Este capítulo presenta un estudio de caso con ejercicios que le permiten pensar en elegir estructuras de
datos y practicar su uso.
13.1 Análisis de frecuencia de palabras
Como de costumbre, al menos debería intentar los ejercicios antes de leer mis soluciones.
Ejercicio 13.1. Escriba un programa que lea un archivo, divida cada línea en palabras, elimine los espacios en
blanco y la puntuación de las palabras y las convierta a minúsculas.
Además, podría considerar usar los métodos de cadena , y ) .
Ejercicio 13.2. Vaya a Proyecto Gutenberg (libro y descarga tu favorito
sin derechos de autor en formato de texto sin formato.
Modifique su programa del ejercicio anterior para leer el libro que descargó, omita la información del
encabezado al principio del archivo y procese el resto de las palabras como antes.
Luego modifique el programa para contar el número total de palabras en el libro y el número de veces que
se usa cada palabra.
Escribe el número de palabras diferentes usadas en el libro. Compara diferentes libros de diferentes autores,
escritos en diferentes épocas. ¿Qué autor utiliza el vocabulario más extenso?
Machine Translated by Google
126 Capítulo 13. Estudio de caso: selección de estructura de datos
Ejercicio 13.3. Modifique el programa del ejercicio anterior para imprimir las 20 palabras más utilizadas en el libro.
Ejercicio 13.4. Modifique el programa anterior para leer una lista de palabras (vea la Sección 9.1) y luego imprima
todas las palabras del libro que no están en la lista de palabras. ¿Cuántos de ellos son errores tipográficos? ¿Cuántas
de ellas son palabras comunes que deberían estar en la lista de palabras y cuántas de ellas son realmente oscuras?
13.2 Números aleatorios
Dadas las mismas entradas, la mayoría de los programas de computadora generan las mismas salidas cada vez,
por lo que se dice que son deterministas. El determinismo suele ser algo bueno, ya que esperamos que el mismo
cálculo arroje el mismo resultado. Sin embargo, para algunas aplicaciones, queremos que la computadora sea
impredecible. Los juegos son un ejemplo obvio, pero hay más.
Hacer que un programa sea verdaderamente no determinista resulta difícil, pero hay maneras de hacer que al menos
parezca no determinista. Una de ellas es utilizar algoritmos que generen números pseudoaleatorios . Los números
pseudoaleatorios no son verdaderamente aleatorios porque se generan mediante un cálculo determinista, pero con
solo mirar los números es casi imposible distinguirlos de los aleatorios.
El módulo proporciona funciones que generan números pseudoaleatorios (a los que simplemente llamaré "aleatorios"
de ahora en adelante).
La función devuelve un flotante aleatorio entre 0,0 y 1,0 (incluido 0,0 pero no 1,0). , obtienes el
Cada vez que llamas a siguiente número en una serie larga. Para ver una muestra, ejecute
este bucle:
La función toma los parámetros y y (incluidos ambos). y devuelve un entero entre
Para elegir un elemento de una secuencia al azar, puede utilizar :
El módulo también proporciona funciones para generar valores aleatorios a partir de distribuciones
continuas, incluidas gaussianas, exponenciales, gamma y algunas más.
Ejercicio 13.5. Escriba una función llamada que tome un histograma como se define en la Sección 11.2 y devuelva
un valor aleatorio del histograma, elegido con probabilidad en proporción a la frecuencia. Por ejemplo, para este
histograma:
Machine Translated by Google
13.3. Histograma de palabras 127
13.3 Histograma de palabras
Debe intentar los ejercicios anteriores antes de continuar. Puede descargar mi solución desde . también
necesitarás
.
Aquí hay un programa que lee un archivo y construye un histograma de las palabras en el archivo:
Este programa lee , que contiene el texto de Emma de Jane Austen.
recorre las líneas del archivo, pasándolas una a la vez a . El histograma se utiliza como
acumulador.
usa el método de cadena para reemplazar los guiones con espacios antes de usar para
dividir la línea en una lista de cadenas. Recorre la lista de palabras y usos
y las para eliminar la puntuación y convertir a minúsculas. (Es una forma abreviada de decir eso y
cadenas se "convierten"; recuerde que las cadenas son inmutables, por lo que los métodos como
devolver nuevas cadenas).
actualiza el histograma creando un nuevo elemento o incrementando un
Finalmente, uno existente.
Para contar el número total de palabras en el archivo, podemos sumar las frecuencias en el histograma:
Machine Translated by Google
128 Capítulo 13. Estudio de caso: selección de estructura de datos
La cantidad de palabras diferentes es solo la cantidad de elementos en el diccionario:
Aquí hay un código para imprimir los resultados:
Y los resultados:
13.4 Palabras más comunes
Para encontrar las palabras más comunes, podemos hacer una lista de tuplas, donde cada tupla contiene una palabra y
su frecuencia, y ordenarla.
La siguiente función toma un histograma y devuelve una lista de tuplas de frecuencia de palabra:
En cada tupla, la frecuencia aparece primero, por lo que la lista resultante se ordena por frecuencia. Aquí hay un bucle
que imprime las diez palabras más comunes:
Utilizo el argumento de palabra clave para indicar que se utilice un carácter de tabulación como "separador", en lugar de
un espacio, de modo que la segunda columna esté alineada. Estos son los resultados de Emma:
13.5. Parámetros opcionales 129
13.5 Parámetros opcionales
Hemos visto funciones y métodos incorporados que toman argumentos opcionales. También es posible escribir
funciones definidas por el programador con argumentos opcionales. Por ejemplo, aquí hay una función que
imprime las palabras más comunes en un histograma
El primer parámetro es obligatorio; el segundo es opcional. El valor predeterminado de es 10
Si solo proporciona un argumento:
obtiene el valor predeterminado. Si proporcionas dos argumentos:
obtiene el valor del argumento en su lugar. En otras palabras, el argumento opcional sobre
monta el valor predeterminado.
Si una función tiene parámetros requeridos y opcionales, todos los parámetros requeridos deben ir primero,
seguidos de los opcionales.
13.6 Resta de diccionario
Encontrar las palabras del libro que no están en la lista de palabras es un problema que puede reconocer como
resta de conjuntos; es decir, queremos encontrar todas las palabras de un conjunto (las palabras del libro) que no
están en el otro (las palabras de la lista).
toma diccionarios y devuelve un nuevo diccionario que contiene todos los . Como realmente no nos
claves de eso no están en Ninguno. importan los valores, los configuramos todos
un histograma para , y luego restar:
Estos son algunos de los resultados de Emma:
Machine Translated by Google
130 Capítulo 13. Estudio de caso: selección de estructura de datos
Algunas de estas palabras son nombres y posesivos. Otros, como “rencontre”, ya no son de uso común. ¡Pero
algunas son palabras comunes que realmente deberían estar en la lista!
Ejercicio 13.6. Python proporciona una estructura de datos llamada que proporciona muchas operaciones de
conjuntos comunes. Puede leer sobre ellos en la Sección 19.5, o leer la documentación en
.
Escribe un programa que use la resta de conjuntos para encontrar palabras en el libro que no están en la lista de palabras.
Solución: .
13.7 Palabras aleatorias
Para elegir una palabra aleatoria del histograma, el algoritmo más simple es construir una lista con múltiples
copias de cada palabra, de acuerdo con la frecuencia observada, y luego elegir de la lista:
Este algoritmo funciona, pero no es muy eficiente; cada vez que elige una palabra al azar, se reconstruye la lista,
que es tan grande como el libro original. Una mejora obvia es crear la lista una vez y luego hacer varias
selecciones, pero la lista sigue siendo grande.
Una alternativa es:
1. Uso para obtener una lista de las palabras en el libro.
2. Construya una lista que contenga la suma acumulada de las frecuencias de las palabras (vea el Ejercicio
10.2). El último elemento de esta lista es el número total de palabras del libro, n.
3. Elige un número aleatorio del 1 al n. Use una búsqueda de bisección (vea el ejercicio 10.10) para encontrar
el índice donde se insertaría el número aleatorio en la suma acumulada.
4. Use el índice para encontrar la palabra correspondiente en la lista de palabras.
Ejercicio 13.7. Escriba un programa que use este algoritmo para elegir una palabra al azar del libro.
Solución: .
13.8 Análisis de Markov
Si elige palabras del libro al azar, puede tener una idea del vocabulario, pero probablemente no obtendrá una
oración:
Machine Translated by Google
13.8. análisis de Markov 131
Una serie de palabras al azar rara vez tiene sentido porque no hay relación entre las palabras sucesivas. Por
ejemplo, en una oración real, esperaría que un artículo como "el" fuera seguido por un adjetivo o un sustantivo, y
probablemente no por un verbo o adverbio.
Una forma de medir este tipo de relaciones es el análisis de Markov, que caracteriza, para una secuencia dada
de palabras, la probabilidad de que aparezcan las siguientes palabras. Por ejemplo, la canción Eric, the Half a
Bee comienza:
Media abeja, filosóficamente,
Debe, ipso facto, la mitad no ser.
Pero la mitad de la abeja tiene que
ser Vis a vis, su entidad. ¿Ves?
Pero, ¿puede decirse que una
abeja es o no una abeja entera
cuando la mitad de la abeja no es
abeja debido a alguna herida antigua?
En este texto, la frase "la mitad de" siempre va seguida de la palabra "abeja", pero la frase "la abeja" puede ir
seguida de "tiene" o "es".
El resultado del análisis de Markov es un mapeo de cada prefijo (como "la mitad de" y "la abeja") a todos los
sufijos posibles (como "tiene" y "es").
Dada esta asignación, puede generar un texto aleatorio comenzando con cualquier prefijo y eligiendo al azar
entre los posibles sufijos. A continuación, puede combinar el final del prefijo y el nuevo sufijo para formar el
siguiente prefijo y repetir.
Por ejemplo, si comienza con el prefijo "Half a", entonces la siguiente palabra tiene que ser "bee", porque el prefijo
solo aparece una vez en el texto. El siguiente prefijo es "una abeja", por lo que el siguiente sufijo podría ser
"filosóficamente", "ser" o "debido".
En este ejemplo, la longitud del prefijo siempre es dos, pero puede realizar un análisis de Markov con cualquier
longitud de prefijo.
Ejercicio 13.8. Análisis de Markov:
1. Escriba un programa para leer un texto de un archivo y realizar un análisis de Markov. El resultado debería
ser un diccionario que mapee desde prefijos a una colección de posibles sufijos. La colección puede ser
una lista, una tupla o un diccionario; depende de usted hacer una elección adecuada. Puede probar su
programa con la longitud de prefijo dos, pero debe escribir el programa de una manera que facilite probar
otras longitudes.
2. Agregue una función al programa anterior para generar texto aleatorio basado en el análisis de Markov.
Aquí hay un ejemplo de Emma con prefijo de longitud 2: Él
fue muy inteligente, ya sea por dulzura o por estar enojado, avergonzado o solo divertido, ante
tal golpe. ¿Nunca había pensado en Hannah hasta que nunca estuviste destinado para mí? —
No puedo pronunciar discursos, Emma: —pronto se cortó todo—.
Para este ejemplo, dejé la puntuación adjunta a las palabras. El resultado es casi sintácticamente correcto,
pero no del todo. Semánticamente, casi tiene sentido, pero no del todo.
¿Qué sucede si aumenta la longitud del prefijo? ¿Tiene más sentido el texto aleatorio?
Machine Translated by Google
132 Capítulo 13. Estudio de caso: selección de estructura de datos
3. Una vez que su programa esté funcionando, es posible que desee probar una combinación: si
combina texto de dos o más libros, el texto aleatorio que genere combinará el vocabulario y las
frases de las fuentes de maneras interesantes.
Crédito: Este estudio de caso se basa en un ejemplo de Kernighan y Pike, The Practice of Pro gramming,
AddisonWesley, 1999.
Debe intentar este ejercicio antes de continuar; entonces puedes descargar mi solución de
. también necesitarás
.
13.9 Estructuras de datos
Usar el análisis de Markov para generar texto aleatorio es divertido, pero también hay un punto en este
ejercicio: la selección de la estructura de datos. En tu solución a los ejercicios anteriores, tuviste que elegir:
• Cómo representar los prefijos.
• Cómo representar la colección de posibles sufijos.
• Cómo representar el mapeo de cada prefijo a la colección de posibles sufijos.
El último es fácil: un diccionario es la opción obvia para un mapeo de claves a valores correspondientes.
Para los prefijos, las opciones más obvias son cadena, lista de cadenas o tupla de cadenas.
Para los sufijos, una opción es una lista; otro es un histograma (diccionario).
¿Cómo debes elegir? El primer paso es pensar en las operaciones que necesitará implementar para cada
estructura de datos. Para los prefijos, debemos poder eliminar palabras del principio y agregarlas al final.
Por ejemplo, si el prefijo actual es "Half a", y la siguiente palabra es "bee", debe poder formar el siguiente
prefijo, "a bee".
Su primera opción podría ser una lista, ya que es fácil agregar y eliminar elementos, pero también debemos
poder usar los prefijos como claves en un diccionario, de modo que descarte las listas. Con las tuplas, no
puede agregar ni eliminar, pero puede usar el operador de suma para formar una nueva tupla:
,
toma una tupla de palabras y una cadena, , tupla que tiene todas las palabras
y forma una nueva
excepto la primera y se agrega al final.
Para la recopilación de sufijos, las operaciones que debemos realizar incluyen agregar un nuevo sufijo (o
aumentar la frecuencia de uno existente) y elegir un sufijo aleatorio.
Agregar un nuevo sufijo es igualmente fácil para la implementación de la lista o el histograma. Elegir un
elemento aleatorio de una lista es fácil; elegir de un histograma es más difícil de hacer de manera eficiente
(vea el ejercicio 13.7).
Hasta ahora hemos estado hablando principalmente sobre la facilidad de implementación, pero hay otros
factores a considerar al elegir estructuras de datos. Uno es el tiempo de ejecución. A veces hay una razón
teórica para esperar que una estructura de datos sea más rápida que otra; por ejemplo, mencioné que
Machine Translated by Google
13.10. depuración 133
el operador es más rápido para diccionarios que para listas, al menos cuando el número de elementos es grande.
Pero a menudo no se sabe de antemano qué implementación será más rápida. Una opción es implementar
ambos y ver cuál es mejor. Este enfoque se llama evaluación comparativa.
Una alternativa práctica es elegir la estructura de datos que sea más fácil de implementar y luego ver si es lo
suficientemente rápida para la aplicación prevista. Si es así, no hay necesidad de continuar. Si no, hay
herramientas, como el módulo, que pueden identificar los lugares en un programa que toman la mayor parte del
tiempo.
El otro factor a considerar es el espacio de almacenamiento. Por ejemplo, usar un histograma para la colección
de sufijos puede ocupar menos espacio porque solo tiene que almacenar cada palabra una vez, sin importar
cuántas veces aparezca en el texto. En algunos casos, ahorrar espacio también puede hacer que su programa
se ejecute más rápido y, en casos extremos, es posible que su programa no se ejecute en absoluto si se queda
sin memoria. Pero para muchas aplicaciones, el espacio es una consideración secundaria después del tiempo
de ejecución.
Una reflexión final: en esta discusión, he dado a entender que debemos usar una estructura de datos tanto para
el análisis como para la generación. Pero dado que se trata de fases separadas, también sería posible utilizar
una estructura para el análisis y luego convertirla en otra estructura para la generación.
Esto sería una ganancia neta si el tiempo ahorrado durante la generación excediera el tiempo dedicado a la
conversión.
13.10 Depuración
Cuando está depurando un programa, y especialmente si está trabajando en un error grave, hay cinco cosas
que puede probar:
Lectura: examina tu código, léelo para ti mismo y comprueba que dice lo que
quise decir.
Ejecución: experimente haciendo cambios y ejecutando diferentes versiones. A menudo, si muestra lo correcto
en el lugar correcto del programa, el problema se vuelve obvio, pero a veces tiene que construir un
andamiaje.
Rumiar: ¡ Tómate un tiempo para pensar! ¿Qué tipo de error es: sintaxis, tiempo de ejecución o semántico?
¿Qué información puede obtener de los mensajes de error o de la salida del programa? ¿Qué tipo de
error podría causar el problema que estás viendo? ¿Qué fue lo último que cambió, antes de que
apareciera el problema?
Dulzura: si le explicas el problema a otra persona, a veces encuentras la respuesta antes de terminar de hacer
la pregunta. A menudo no necesitas a la otra persona; podrías simplemente hablar con un patito de goma.
Y ese es el origen de la conocida estrategia llamada depuración del patito de goma. No estoy inventando
esto; ver
.
Retiro: en algún momento, lo mejor que puede hacer es retroceder, deshaciendo los cambios recientes, hasta
que vuelva a tener un programa que funcione y que entienda. Entonces puedes empezar a reconstruir.
Machine Translated by Google
134 Capítulo 13. Estudio de caso: selección de estructura de datos
Los programadores principiantes a veces se atascan en una de estas actividades y se olvidan de las demás. Cada
actividad viene con su propio modo de falla.
Por ejemplo, leer su código puede ayudar si el problema es un error tipográfico, pero no si el problema es un
malentendido conceptual. Si no entiende lo que hace su programa, puede leerlo 100 veces y nunca ver el error,
porque el error está en su cabeza.
Ejecutar experimentos puede ayudar, especialmente si ejecuta pruebas pequeñas y simples. Pero si ejecuta
experimentos sin pensar o leer su código, puede caer en un patrón que llamo "programación de caminata aleatoria",
que es el proceso de hacer cambios aleatorios hasta que el programa hace lo correcto. No hace falta decir que la
programación de caminata aleatoria puede llevar mucho tiempo.
Tienes que tomarte un tiempo para pensar. La depuración es como una ciencia experimental. Debe tener al menos
una hipótesis sobre cuál es el problema. Si hay dos o más posibilidades, trate de pensar en una prueba que elimine
una de ellas.
Pero incluso las mejores técnicas de depuración fallarán si hay demasiados errores o si el código que está tratando
de corregir es demasiado grande y complicado. A veces la mejor opción es retirarse, simplificando el programa
hasta llegar a algo que funcione y que entiendas.
Los programadores principiantes a menudo son reacios a retirarse porque no soportan borrar una línea de código
(incluso si está mal). Si te hace sentir mejor, copia tu programa en otro archivo antes de empezar a desmontarlo.
Luego puede copiar las piezas de nuevo una a la vez.
Encontrar un error difícil requiere leer, correr, reflexionar y, a veces, retirarse. Si te quedas atascado en una de
estas actividades, prueba las otras.
13.11 Glosario
determinista: Perteneciente a un programa que hace lo mismo cada vez que se ejecuta, dadas las mismas entradas.
pseudoaleatorio: Perteneciente a una secuencia de números que parece ser aleatoria, pero es generada por un
programa determinista.
valor predeterminado: el valor dado a un parámetro opcional si no se proporciona ningún argumento.
override: para reemplazar un valor predeterminado con un argumento.
evaluación comparativa: el proceso de elegir entre estructuras de datos implementando alternativas y probándolas
en una muestra de las posibles entradas.
Depuración de patitos de goma: Depuración explicando su problema a un objeto inanimado como un patito de
goma. Articular el problema puede ayudarte a resolverlo, incluso si el patito de goma no conoce Python.
13.12 Ejercicios
Ejercicio 13.9. El “rango” de una palabra es su posición en una lista de palabras ordenadas por frecuencia: la
palabra más común tiene el rango 1, la segunda más común tiene el rango 2, etc.
Machine Translated by Google
13.12. Ejercicios 135
La ley de Zipf describe una relación entre los rangos y las frecuencias de las palabras en los lenguajes naturales
( ). En concreto, predice que la frecuencia, f , de la palabra con rango r es:
f = cr−s
donde s y c son parámetros que dependen del idioma y del texto. Si tomas el logaritmo de ambos lados de esta
ecuación, obtienes:
log f = log c − slog r
Entonces, si graficas log f versus log r, deberías obtener una línea recta con pendiente −s e interceptar log c.
Escriba un programa que lea un texto de un archivo, cuente la frecuencia de las palabras e imprima una línea para
cada palabra, en orden descendente de frecuencia, con log f y log r. Utilice el programa de gráficos de su elección
para trazar los resultados y verifique si forman una línea recta. ¿Puedes estimar el valor de s?
Solución: . Para ejecutar mi solución, necesita la
el módulo ting trama. Si instaló Anaconda, ya tiene ; de lo contrario, tú
podría tener que instalarlo.
Machine Translated by Google
136 Capítulo 13. Estudio de caso: selección de estructura de datos
Machine Translated by Google
capitulo 14
archivos
Este capítulo presenta la idea de los programas “persistentes” que mantienen los datos en almacenamiento permanente
y muestra cómo usar diferentes tipos de almacenamiento permanente, como archivos y bases de datos.
14.1 Persistencia
La mayoría de los programas que hemos visto hasta ahora son transitorios en el sentido de que se ejecutan durante un
breve período de tiempo y producen algún resultado, pero cuando finalizan, sus datos desaparecen. Si ejecuta el
programa de nuevo, comienza con una pizarra limpia.
Otros programas son persistentes: se ejecutan durante mucho tiempo (o todo el tiempo); mantienen al menos algunos
de sus datos en almacenamiento permanente (un disco duro, por ejemplo); y si se apagan y se reinician, continúan
donde lo dejaron.
Ejemplos de programas persistentes son los sistemas operativos, que se ejecutan casi siempre que una computadora
está encendida, y los servidores web, que se ejecutan todo el tiempo, esperando que lleguen solicitudes a la red.
Una de las formas más sencillas para que los programas mantengan sus datos es leyendo y escribiendo archivos de
texto. Ya hemos visto programas que leen archivos de texto; en este capítulo veremos programas que los escriben.
Una alternativa es almacenar el estado del programa en una base de datos. En este capítulo presentaré una base de
datos simple y un módulo, , que facilita el almacenamiento de datos del programa.
14.2 Leer y escribir
Un archivo de texto es una secuencia de caracteres almacenada en un medio permanente como un disco duro, una
memoria flash o un CDROM. Vimos cómo abrir y leer un archivo en la Sección 9.1.
Para escribir un archivo, debe abrirlo con el modo como segundo parámetro:
Machine Translated by Google
138 Capítulo 14. Archivos
Si el archivo ya existe, abrirlo en modo de escritura borra los datos antiguos y comienza de nuevo, ¡así que tenga
cuidado! Si el archivo no existe, se crea uno nuevo.
devuelve un objeto de archivo que proporciona métodos para trabajar con el archivo. El
método pone datos en el archivo.
El valor devuelto es el número de caracteres que se escribieron. El objeto de archivo realiza un seguimiento de
dónde está, por lo que si vuelve a llamar, agrega los nuevos datos al final del archivo.
Cuando haya terminado de escribir, debe cerrar el archivo.
Si no cierra el archivo, se cerrará cuando finalice el programa.
14.3 Operador de formato
El argumento de tener tiene que ser una cadena, así que si queremos poner otros valores en un archivo,
que convertirlos en cadenas. La forma más fácil de hacerlo es con:
Una alternativa es utilizar el operador de formato, . Cuando se aplica a números enteros, es el operador de módulo.
Pero cuando el primer operando es una cadena, es el operador de formato.
El primer operando es la cadena de formato, que contiene una o más secuencias de formato, que especifican cómo
se formatea el segundo operando. El resultado es una cadena.
Por ejemplo, la secuencia de formato ted significa que el segundo operando debe tener formato
como un entero decimal:
El resultado es la cadena. , que no debe confundirse con el valor entero.
Una secuencia de formato puede aparecer en cualquier lugar de la cadena, por lo que puede incrustar un valor en
una oración:
Si hay más de una secuencia de formato en la cadena, el segundo argumento debe ser una tupla. Cada secuencia
de formato se empareja con un elemento de la tupla, en orden.
14.4. Nombres de archivos y rutas 139
El número de elementos en la tupla tiene que coincidir con el número de secuencias de formato en la
cadena. Además, los tipos de los elementos deben coincidir con las secuencias de formato:
En el primer ejemplo, no hay suficientes elementos; en el segundo, el elemento es del tipo incorrecto.
Para obtener más información sobre el operador de formato, consulte
. Una alternativa más poderosa es
el método de formato de cadena, sobre el que puede leer en
.
14.4 Nombres de archivos y rutas
Los archivos se organizan en directorios (también llamados "carpetas"). Cada programa en ejecución
tiene un "directorio actual", que es el directorio predeterminado para la mayoría de las operaciones. Por
ejemplo, cuando abre un archivo para leerlo, Python lo busca en el directorio actual.
El módulo proporciona funciones para trabajar con archivos y directorios ("os" significa "sistema operativo").
devuelve el nombre del directorio actual:
significa "directorio de trabajo actual". El resultado en este ejemplo es cuál es ,
el directorio de inicio de un usuario llamado .
Una cadena como que identifica un archivo o directorio se llama ruta.
Un nombre de archivo también se considera una ruta, pero es una ruta relativa porque el
simple, como si se relacionara con el directorio actual. Si el directorio actual es , nombre del archivo
se referiría a .
Una ruta que comienza con no depende del directorio actual; se llama un camino absoluto. Para encontrar
la ruta absoluta a un archivo, puede usar :
proporciona otras funciones para trabajar con nombres de archivos y rutas. Por ejemplo,
comprueba si existe un archivo o directorio:
Si existiera, comprueba si es un directorio:
Machine Translated by Google
140 Capítulo 14. Archivos
Similarmente, comprueba si es un archivo.
devuelve una lista de los archivos (y otros directorios) en el directorio dado:
Para demostrar estas funciones, el siguiente ejemplo "camina" a través de un directorio, imprime los
nombres de todos los archivos y se llama a sí mismo recursivamente en todos los directorios.
toma un directorio y un nombre de archivo y los une en una ruta completa.
El módulo proporciona una función llamada que es similar a este pero más ver
satile. Como ejercicio, lea la documentación y utilícela para imprimir los nombres de los archivos en un
directorio dado y sus subdirectorios. Puedes descargar mi solución desde
.
14.5 Captura de excepciones
Muchas cosas pueden salir mal cuando intentas leer y escribir archivos. Si intenta abrir un archivo que
no existe, obtiene un :
Si no tiene permiso para acceder a un archivo:
Y si intenta abrir un directorio para leer, obtiene
Para evitar estos errores, puede usar funciones como y ,
”
pero se necesitaría mucho tiempo y código para verificar todas las posibilidades (si es cualquier
" indica, hay al menos 21 cosas que pueden salir mal).
Es mejor seguir adelante e intentar, y lidiar con los problemas si ocurren, que es exactamente lo que
hace la declaración. La sintaxis es similar a una declaración:
Machine Translated by Google
14.6. bases de datos 141
Manejar una excepción con una declaración se llama capturar una excepción. En este ejemplo, la cláusula
muestra un mensaje de error que no es muy útil. En general, detectar una excepción le brinda la
oportunidad de solucionar el problema, o intentarlo de nuevo, o al menos finalizar el programa correctamente.
14.6 Bases de datos
Una base de datos es un archivo que está organizado para almacenar datos. Muchas bases de datos están
organizadas como un diccionario en el sentido de que asignan claves a valores. La mayor diferencia entre una
base de datos y un diccionario es que la base de datos está en el disco (u otro almacenamiento permanente), por
lo que persiste después de que finaliza el programa.
El módulo proporciona una interfaz para crear y actualizar archivos de base de datos. Como ejemplo,
crearé una base de datos que contenga subtítulos para archivos de imágenes.
Abrir una base de datos es similar a abrir otros archivos:
El modo significa que la base de datos debe crearse si aún no existe. El resultado es un objeto de base de
datos que se puede usar (para la mayoría de las operaciones) como un diccionario.
Cuando creas un nuevo elemento, actualiza el archivo de la base de datos.
Cuando acceda a uno de los elementos, lee el archivo:
El resultado es un objeto de bytes, por lo que comienza con . Un objeto de bytes es similar a
una cadena en muchos aspectos. Cuando profundizas en Python, la diferencia se vuelve importante, pero
por ahora podemos ignorarla.
Si realiza otra asignación a una clave existente, reemplaza el valor anterior:
Algunos métodos de diccionario, como y , no trabaje con objetos de base de datos. Pero
la iteración con un bucle, funcionan:
Al igual que con otros archivos, debe cerrar la base de datos cuando haya terminado:
Machine Translated by Google
142 Capítulo 14. Archivos
14.7 Decapado
Una limitación de es que las claves y valores tienen que ser cadenas o bytes. Si intentas usar
cualquier otro tipo, te da un error.
El módulo puede ayudar. Traduce casi cualquier tipo de objeto en una cadena adecuada para el almacenamiento en
una base de datos y luego convierte las cadenas nuevamente en objetos.
toma un objeto como parámetro y devuelve una representación de cadena ( es
abreviatura de "cadena de volcado"):
El formato no es obvio para los lectores humanos; está destinado a ser fácil para ("cadena interpretar.
de carga") reconstituye el objeto:
Aunque el nuevo objeto tiene el mismo valor que el anterior, no es (en general) el mismo objeto:
En otras palabras, decapar y luego decapar tiene el mismo efecto que copiar el objeto.
Puede usar para almacenar no cadenas en una base de datos. De hecho, esta combinación es tan com
mon que ha sido encapsulado en un módulo llamado .
14.8 Tuberías
La mayoría de los sistemas operativos proporcionan una interfaz de línea de comandos, también conocida como shell.
Los shells suelen proporcionar comandos para navegar por el sistema de archivos y ejecutar aplicaciones. Por
ejemplo, en Unix puede cambiar directorios con , mostrar el contenido de un directorio e iniciar un navegador web ,
escribiendo (por ejemplo) .
Cualquier programa que pueda iniciar desde el shell también puede iniciarse desde Python utilizando un objeto de
canalización, que representa un programa en ejecución.
Por ejemplo, la rectoría de comandos de normalmente muestra el contenido del di actual
Unix en formato largo. Puedes lanzar con 1 :
1
está en desuso ahora, lo que significa que se supone que debemos dejar de usarlo y comenzar a usar el
módulo. Pero para casos sencillos, más complicado de lo necesario. Así que voy a seguir usando
encuentro hasta que me lo quitan.
Machine Translated by Google
14.9. Módulos de escritura 143
El argumento es una cadena que contiene un comando de shell. El valor de retorno es un objeto que se
comporta como un archivo abierto. Puede leer la salida del proceso una línea a la vez u obtener todo de una
vez con :
Cuando haya terminado, cierre la tubería como un archivo:
El valor devuelto es el estado final del proceso; (sin errores). significa que termino normalmente
Por ejemplo, la mayoría de los sistemas Unix proporcionan un comando que lee el contenido
llamado de un archivo y calcula una "suma de verificación". Puede leer sobre
MD5 en . Este comando proporciona una forma eficaz de comprobar si dos archivos tienen el
mismo contenido. La probabilidad de que diferentes contenidos produzcan la misma suma de verificación es
muy pequeña (es decir, es poco probable que suceda antes de que el universo colapse).
Puedes usar una tubería para correr de Python y obtener el resultado:
14.9 Módulos de escritura
Cualquier archivo que contenga código de Python se puede importar como un módulo. Por ejemplo, suponga
que tiene un archivo llamado con el siguiente código:
Si ejecuta este programa, se lee solo e imprime el número de líneas en el archivo, que es 7.
También puedes importarlo así:
Ahora tienes un objeto de módulo:
Machine Translated by Google
144 Capítulo 14. Archivos
El objeto del módulo proporciona :
Así es como escribes módulos en Python.
El único problema con este ejemplo es que cuando importa el módulo, ejecuta el código de prueba en la
parte inferior. Normalmente, cuando importa un módulo, define nuevas funciones pero no las ejecuta.
Los programas que se importarán como módulos a menudo usan la siguiente expresión:
es una variable integrada que se establece cuando se inicia el programa. Si el programa se está ejecutando
de comandos, tiene el valor ; en ese caso, se ejecuta el código de prueba. De lo contrario, como secuencia
si se está importando el módulo, se omite el código de prueba.
Como ejercicio, escriba este ejemplo en un archivo llamado y ejecutarlo como un script. Entonces
intérprete de Python y ¿se está corre . ¿Cuál es el valor de cuando el módulo es
importando?
Advertencia: si importa un módulo que ya se ha importado, Python no hace nada. No vuelve a leer el archivo,
incluso si ha cambiado.
,
Si desea volver a cargar un módulo, puede usar la función incorporada, pero puede ser complicado,
por lo
que lo más seguro es reiniciar el intérprete y luego importar el módulo nuevamente.
14.10 Depuración
Cuando lee y escribe archivos, es posible que tenga problemas con los espacios en blanco.
Estos errores pueden ser difíciles de depurar porque los espacios, tabulaciones y saltos de línea normalmente son invisibles:
La función integrada puede ayudar. Toma cualquier objeto como argumento y devuelve una representación
de cadena del objeto. Para cadenas, representa caracteres de espacio en blanco con secuencias de barra
invertida:
Esto puede ser útil para la depuración.
Otro problema con el que te puedes encontrar es que diferentes sistemas usan diferentes caracteres para
indicar el final de una línea. Algunos sistemas usan una nueva línea, representada . Otros usan un carácter
de retorno, representado . Algunos usan ambos. Si mueve archivos entre diferentes sistemas, estas
inconsistencias pueden causar problemas.
Para la mayoría de los sistemas, existen aplicaciones para convertir de un formato a otro. Puede encontrarlos
(y leer más sobre este tema) en O, por supuesto, puede .
escribir uno usted mismo.
Machine Translated by Google
14.11. Glosario 145
14.11 Glosario
persistente: Perteneciente a un programa que se ejecuta indefinidamente y mantiene al menos parte de su
datos en almacenamiento permanente.
operador de formato: un operador, , que toma una cadena de formato y una tupla y genera una cadena que incluye los
elementos de la tupla con el formato especificado por la cadena de formato.
cadena de formato: Una cadena, utilizada con el operador de formato, que contiene secuencias de formato.
secuencia de formato: Se debe formatear una secuencia de caracteres en una cadena de , que especifica cómo un
formato, como el valor.
archivo de texto: Una secuencia de caracteres almacenados en almacenamiento permanente como un disco duro.
directorio: una colección de archivos con nombre, también llamada carpeta.
ruta: una cadena que identifica un archivo.
ruta relativa: Una ruta que comienza desde el directorio actual.
ruta absoluta: una ruta que comienza desde el directorio superior en el sistema de archivos.
catch: para evitar que una excepción finalice un programa utilizando las teclas y estado
mentos.
base de datos: Un archivo cuyo contenido está organizado como un diccionario con claves que corresponden
a los valores
bytes object: Un objeto similar a una cadena.
shell: Un programa que permite a los usuarios escribir comandos y luego los ejecuta iniciando otros programas.
objeto de tubería: un objeto que representa un programa en ejecución, lo que permite que un programa de Python ejecute
comandos y lea los resultados.
14.12 Ejercicios
Ejercicio 14.1. Escriba una función llamada que tome como argumentos una cadena patrón, una cadena de reemplazo y dos
nombres de archivo; debe leer el primer archivo y escribir el contenido en el segundo archivo (creándolo si es necesario). Si la
cadena de patrón aparece en cualquier parte del archivo, debe reemplazarse con la cadena de reemplazo.
Si ocurre un error al abrir, leer, escribir o cerrar archivos, su programa debe detectar la excepción, imprimir un mensaje de error
y salir. Solución:
.
Ejercicio 14.2. Si descarga mi solución al Ejercicio 12.2 desde , verá que crea un
diccionario que mapea desde una cadena ordenada de mapas al
letras a la lista de palabras que se pueden escribir con esas letras. Por ejemplo, lista
.
Escriba un módulo que importe y proporcione dos nuevas funciones: debe almacenar el diccionario de
anagramas en un "estante"; una lista de sus anagramas. debe buscar una palabra y regresar
Solución: .
Machine Translated by Google
146 Capítulo 14. Archivos
Ejercicio 14.3. En una gran colección de archivos MP3, puede haber más de una copia de la misma canción, almacenada en
diferentes directorios o con diferentes nombres de archivo. El objetivo de este ejercicio es buscar duplicados.
1. Escriba un programa que busque un directorio y todos sus subdirectorios, recursivamente, y devuelva una lista de rutas
completas para todos los archivos con un sufijo dado (como ). Sugerencia: proporciona varias funciones útiles para
manipular archivos y nombres de rutas.
2. Para reconocer duplicados, puede usar archivos para calcular una "suma de comprobación" para cada archivo. si dos
que tengan la misma suma de verificación, probablemente tengan el mismo contenido.
3. Para verificar, puede usar el comando Unix .
Solución: .
Machine Translated by Google
Capítulo 15
clases y objetos
En este punto, sabe cómo usar funciones para organizar código y tipos integrados para organizar datos. El
siguiente paso es aprender "programación orientada a objetos", que utiliza tipos definidos por el programador
para organizar tanto el código como los datos. La programación orientada a objetos es un gran tema; tomará
algunos capítulos para llegar allí.
Los ejemplos de código de este capítulo están disponibles en ; Las
soluciones de los ejercicios están disponibles en
.
15.1 Tipos definidos por el programador
Hemos usado muchos de los tipos integrados de Python; ahora vamos a definir un nuevo tipo. Como ejemplo,
crearemos un tipo llamado que representa un punto en un espacio bidimensional.
En notación matemática, los puntos a menudo se escriben entre paréntesis con una coma que separa las
coordenadas. Por ejemplo, (0, 0) representa el origen y (x, y) representa el punto x unidades a la derecha e y
unidades hacia arriba desde el origen.
Hay varias formas de representar puntos en Python:
• Podríamos almacenar las coordenadas por separado en dos variables, y .
• Podríamos almacenar las coordenadas como elementos en una lista o tupla.
• Podríamos crear un nuevo tipo para representar puntos como objetos.
Crear un nuevo tipo es más complicado que las otras opciones, pero tiene ventajas que se harán evidentes
pronto.
Un tipo definido por el programador también se denomina clase. Una definición de clase se ve así:
Machine Translated by Google
148 Capítulo 15. Clases y objetos
Punto
blanco X 3.0
y 4.0
Figura 15.1: Diagrama de objetos.
El encabezado indica que la nueva clase se llama . El cuerpo es una cadena de documentación que explica para
qué sirve la clase. Puede definir variables y métodos dentro de una definición de clase, pero volveremos a eso más
adelante.
Definición de una clase llamada crea un objeto de clase.
Porque se define en el nivel superior, su "nombre completo" es .
El objeto de clase es como una fábrica para crear objetos. Para crear un Punto, llame a una como si
función.
El valor devuelto es una referencia a un objeto Punto, que asignamos a .
La creación de un nuevo objeto se denomina creación de instancias y el objeto es una instancia de la clase.
Cuando imprime una instancia, Python le dice a qué clase pertenece y dónde está almacenada en la memoria (el
prefijo significa que el siguiente número está en hexadecimal).
Cada objeto es una instancia de alguna clase, por lo que "objeto" e "instancia" son intercambiables.
Pero en este capítulo uso "instancia" para indicar que estoy hablando de un tipo definido por el programador.
15.2 Atributos
Puede asignar valores a una instancia utilizando la notación de puntos:
Esta sintaxis es similar a la sintaxis para seleccionar una variable de un módulo, como en este caso,
o . sin embargo, estamos asignando valores a elementos con nombre de un objeto.
Estos elementos se denominan atributos.
Como sustantivo, “ATtribute” se pronuncia con énfasis en la primera sílaba, a diferencia de “aTRIBute”, que es
un verbo.
La figura 15.1 es un diagrama de estado que muestra el resultado de estas asignaciones. Un diagrama de estado
que muestra un objeto y sus atributos se llama diagrama de objeto.
La variable se se refiere a un objeto Point, que contiene dos atributos. cada atributo
refiere a un número de punto flotante.
Puede leer el valor de un atributo usando la misma sintaxis:
Machine Translated by Google
15.3. Rectángulos 149
La expresión se refiere a y obtiene
significa,
el v"alor
Ir al d
oe .”
bjetoEn el ejemplo, asignamos ese valor a una variable
llamada . No hay conflicto entre la variable y el atributo.
Puede utilizar la notación de puntos como parte de cualquier expresión. Por ejemplo:
Puede pasar una instancia como argumento de la forma habitual. Por ejemplo:
toma un punto como argumento y lo muestra en notación matemática. A como argumento:
invocalo, puedes pasar
Como ejercicio, escribe una función llamada que toma dos Puntos como
argumentos y devuelve la distancia entre ellos.
15.3 Rectángulos
A veces es obvio cuáles deberían ser los atributos de un objeto, pero otras veces hay que tomar
decisiones. Por ejemplo, imagine que está diseñando una clase para representar rectángulos.
¿Qué atributos usaría para especificar la ubicación y el tamaño de un rectángulo? Puede ignorar el
ángulo; para simplificar las cosas, suponga que el rectángulo es vertical u horizontal.
Hay al menos dos posibilidades:
• Puede especificar una esquina del rectángulo (o el centro), el ancho y el
altura.
• Podría especificar dos esquinas opuestas.
En este punto es difícil decir si alguno es mejor que el otro, así que implementaremos el primero, solo
como ejemplo.
Aquí está la definición de clase:
Machine Translated by Google
150 Capítulo 15. Clases y objetos
Rectángulo
caja ancho 100.0
Punto
altura 200.0 X 0.0
esquina
y 0.0
Figura 15.2: Diagrama de objetos.
Para representar un rectángulo, debe instanciar un objeto Rectangle y asignar valores a los atributos:
La expresión significa, “Ir al objeto nombrado; luego vaya a ese hace referencia y seleccione el atributo
objeto y seleccione el atributo llamado ”.
La figura 15.2 muestra el estado de este objeto. Se incrusta un objeto que es un atributo de otro objeto .
15.4 Instancias como valores de retorno
Aquí hay un ejemplo que pasa como argumento y asigna el Punto resultante a
:
Machine Translated by Google
15.5. Los objetos son mutables 151
15.5 Los objetos son mutables
Puede cambiar el estado de un objeto haciendo una asignación a uno de sus atributos. Por ejemplo,
para cambiar el tamaño de un rectángulo sin cambiar su posición, puede modificar los valores de y
:
También puede escribir funciones que modifiquen objetos. Por ejemplo, toma a
objeto Rectángulo y dos números, y ancho y alto del , y suma los números al
rectángulo:
Aquí hay un ejemplo que demuestra el efecto:
Como ejercicio, escriba una función llamada que tome un Rectángulo y dos números llamados y .
Debería cambiar la ubicación del rectángulo sumando a la coordenada de y sumando a la coordenada
de .
15.6 Copia
El alias puede dificultar la lectura de un programa porque los cambios en un lugar pueden tener efectos
inesperados en otro lugar. Es difícil hacer un seguimiento de todas las variables que podrían referirse
a un objeto determinado.
Copiar un objeto suele ser una alternativa a la creación de alias. El módulo contiene una función
llamado que puede duplicar cualquier objeto:
y contienen los mismos datos, pero no son el mismo Punto.
Machine Translated by Google
152 Capítulo 15. Clases y objetos
Figura 15.3: Diagrama de objetos.
El operador indica que y no son el mismo objeto, que es lo que esperábamos. Pero es posible que haya
esperado ceder porque estos puntos contienen los mismos datos. En ese caso, se sentirá decepcionado al
saber que, por ejemplo, el comportamiento predeterminado del operador es el mismo que el del operador;
comprueba la identidad del objeto, no la equivalencia del objeto. Esto se debe a que, para los tipos definidos
por el programador, Python no sabe qué se debería considerar equivalente. Al menos no todavía.
Si usa el para duplicar un Rectángulo, encontrará que copia el Rectángulo
objeto pero no el punto incrustado.
La figura 15.3 muestra cómo se ve el diagrama de objetos. Esta operación se denomina copia superficial
porque copia el objeto y las referencias que contiene, pero no los objetos incrustados.
Para la mayoría de las aplicaciones, esto no es lo que desea. En este ejemplo, invocar a uno de los
Rectángulos no afectaría al otro, ¡pero invocar a cualquiera afectaría a ambos! Este
comportamiento es confuso y propenso a errores.
Afortunadamente, el módulo proporciona un método llamado que copia no solo el objeto sino también los
objetos a los que se refiere, y los objetos a los que se refieren, y así sucesivamente. No te sorprenderá
saber que esta operación se denomina copia profunda.
y son objetos completamente separados.
Como ejercicio, escriba una versión de que crea y devuelve un nuevo Rectángulo
en lugar de modificar la anterior.
15.7 Depuración
Cuando comience a trabajar con objetos, es probable que encuentre algunas excepciones nuevas. Si intenta
acceder a un atributo que no existe, obtiene un :
Machine Translated by Google
15.8. Glosario 153
Si no está seguro de qué tipo es un objeto, puede preguntar:
También puedes usar para comprobar si un objeto es una instancia de una clase:
Si no está seguro de si un objeto tiene un atributo en particular, puede usar la función integrada
:
El primer argumento puede ser cualquier objeto; el segundo argumento es una cadena que contiene el
nombre del atributo.
También puedes usar un instrucción para ver si el objeto tiene los atributos que necesita:
Este enfoque puede facilitar la escritura de funciones que funcionan con diferentes tipos; más sobre ese
tema se presenta en la Sección 17.9.
15.8 Clase del glosario:
Un tipo definido por el programador. Una definición de clase crea un nuevo objeto de clase.
objeto de clase: un objeto que contiene información sobre un tipo definido por el programador. El objeto
de clase se puede utilizar para crear instancias del tipo.
instancia: Un objeto que pertenece a una clase.
instanciar: Para crear un nuevo objeto.
atributo: Uno de los valores con nombre asociados con un objeto.
objeto incrustado: Un objeto que se almacena como un atributo de otro objeto.
copia superficial: para copiar el contenido de un objeto, incluidas las referencias al módulo incrustado.
objetos; implementado por el función en el
copia profunda: para copiar el contenido de un objeto, así como cualquier objeto incrustado, y cualquier
objetos incrustados en ellos, etc.; implementado por el módulo. función en el
diagrama de objetos: un diagrama que muestra objetos, sus atributos y los valores de los at
tributos.
Machine Translated by Google
154 Capítulo 15. Clases y objetos
15.9 Ejercicios
Ejercicio 15.1. Escribe una definición para una clase llamada con atributos y ,
donde es un objeto Punto y es un número.
Cree una instancia de un objeto Circle que represente un círculo con su centro en (150, 100) y un radio de 75.
Escriba una función llamada que tome un círculo y un punto y devuelva verdadero si el punto se encuentra en
o sobre el límite del círculo.
Escriba una función llamada que tome un Círculo y un Rectángulo y devuelva Verdadero si el Rectángulo se encuentra
completamente dentro o sobre el límite del círculo.
Escriba una función llamada que tome un Círculo y un Rectángulo y devuelva Verdadero si alguna de las
esquinas del Rectángulo cae dentro del Círculo. O como una versión más desafiante, devuelva True si alguna
parte del Rectángulo cae dentro del Círculo.
Solución: .
Ejercicio 15.2. Escriba una función llamada que tome un objeto Tortuga y un Rectángulo y use la Tortuga para
dibujar el Rectángulo. Vea el Capítulo 4 para ver ejemplos usando objetos Turtle.
Escribe una función llamada que toma una Tortuga y un Círculo y dibuja el Círculo.
Solución: .
Machine Translated by Google
capitulo 16
clases y funciones
Ahora que sabemos cómo crear nuevos tipos, el siguiente paso es escribir funciones que tomen objetos
definidos por el programador como parámetros y los devuelvan como resultados. En este capítulo
también presento el “estilo de programación funcional” y dos nuevos planes de desarrollo de programas.
Los ejemplos de código de este capítulo están disponibles en .
Las soluciones a los ejercicios están en
.
16.1 Tiempo
Como otro ejemplo de un tipo definido por el programador, definiremos una clase llamada eso
registra la hora del día. La definición de clase se ve así:
Podemos crear un nuevo objeto y asigne atributos para horas, minutos y segundos:
El diagrama de estado de la El objeto se parece a la figura 16.1.
Como ejercicio, escriba una función llamada que tome un objeto Hora y lo imprima en la forma .
Sugerencia: la secuencia de formato imprime un número entero con al menos dos dígitos, incluido un
cero inicial si es necesario.
156 Capítulo 16. Clases y funciones
Tiempo
tiempo hora 11
minuto 59
segundo 30
Figura 16.1: Diagrama de objetos.
16.2 Funciones puras
En las próximas secciones, escribiremos dos funciones que suman valores de tiempo. Demuestran dos tipos de
funciones: funciones puras y modificadores. También demuestran un plan de desarrollo que llamaré prototipo y
parche, que es una forma de abordar un problema complejo comenzando con un prototipo simple y lidiando
gradualmente con las complicaciones.
Aquí hay un prototipo simple de :
La función crea un nuevo objeto, inicializa sus atributos y devuelve una referencia al nuevo objeto. Esto se
denomina función pura porque no modifica ninguno de los objetos que se le pasan como argumentos y no tiene
ningún efecto, como mostrar un valor u obtener la entrada del usuario, aparte de devolver un valor.
Para probar esta función, crearé dos objetos de tiempo: contiene la hora de inicio de una película,
como Monty Python y el Santo Grial, que dura una contiene el tiempo de ejecución de la película,
hora y 35 minutos.
determina cuándo se terminará la película.
El resultado, puede que no sea lo que esperabas. El problema es que esta función no se ocupa de los casos en
los que la cantidad de segundos o minutos suman más de sesenta. Cuando eso sucede, tenemos que "llevar"
los segundos adicionales a la columna de minutos o los minutos adicionales a la columna de horas.
Aquí hay una versión mejorada:
Machine Translated by Google
16.3. modificadores 157
Aunque esta función es correcta, se está empezando a hacer grande. Veremos una alternativa más corta más
adelante.
16.3 Modificadores
A veces es útil que una función modifique los objetos que obtiene como parámetros. En ese caso, los cambios
son visibles para la persona que llama. Las funciones que funcionan de esta manera se llaman modificadores.
, que suma un número dado de segundos a un objeto, se puede escribir naturalmente
como modificador. Aquí hay un borrador:
La primera línea realiza la operación básica; el resto trata de los casos especiales que vimos antes.
¿Es correcta esta función? Qué pasa si es mucho mayor que sesenta?
En ese caso, no basta con llevar una vez; tenemos que seguir haciéndolo hasta que sea menos de sesenta. Una
solución es reemplazar las sentencias por sentencias. Eso haría que la función fuera correcta, pero no muy
eficiente. Como ejercicio, escriba una versión correcta de que no contenga bucles.
Cualquier cosa que se pueda hacer con modificadores también se puede hacer con funciones puras. De hecho,
algunos lenguajes de programación solo permiten funciones puras. Existe alguna evidencia de que los programas
que usan funciones puras son más rápidos de desarrollar y menos propensos a errores que los programas que
usan modificadores. Pero los modificadores son convenientes a veces y los programas funcionales tienden a ser
menos eficientes.
Machine Translated by Google
158 Capítulo 16. Clases y funciones
En general, recomiendo que escriba funciones puras siempre que sea razonable y recurra a modificadores
solo si hay una ventaja convincente. Este enfoque podría llamarse un estilo de programación funcional.
Como ejercicio, escriba una versión "pura" del que crea y devuelve un nuevo Time
objeto en lugar de modificar el parámetro.
16.4 Creación de prototipos frente a planificación
El plan de desarrollo que estoy demostrando se llama "prototipo y parche". Para cada función, escribí un
prototipo que realizaba el cálculo básico y luego lo probaba, corrigiendo errores en el camino.
Este enfoque puede ser efectivo, especialmente si aún no tiene una comprensión profunda del problema. Pero
las correcciones incrementales pueden generar código que es innecesariamente complicado, ya que trata con
muchos casos especiales, y poco confiable, ya que es difícil saber si ha encontrado todos los errores.
Una alternativa es el desarrollo diseñado, en el que una visión de alto nivel del problema puede facilitar mucho
la programación. En este caso, la idea es que un objeto Tiempo es realmente un número de tres dígitos en
base 60 (ver ). El atributo es la "columna de unidades", el atributo es la "columna de sesenta" y el atributo es
la "columna de treinta y seis centenas".
Cuando escribimos y efectivamente estábamos sumando
, en base 60, por lo que tuvimos que llevar de una
columna a la siguiente.
Esta observación sugiere otro enfoque para todo el problema: podemos convertir objetos de tiempo en
números enteros y aprovechar el hecho de que la computadora sabe cómo hacer aritmética de números
enteros.
Aquí hay una función que convierte tiempos a números enteros:
Y aquí hay una función que convierte un número entero en una Hora (recupere divide la primera
ese argumento por segundo y devuelva el cociente y el resto como una tupla).
Es posible que deba pensar un poco y realizar algunas pruebas para convencerse de que estas funciones son
correctas. Una forma de probarlos es comprobar que para muchos valores de . Este es un ejemplo de una
verificación de consistencia.
Una vez que esté convencido de que son correctos, puede usarlos para reescribir :
Machine Translated by Google
16.5. depuración 159
Esta versión es más corta que la original y más fácil de verificar. Como ejercicio, reescribe
usando y .
De alguna manera, convertir de base 60 a base 10 y viceversa es más difícil que simplemente lidiar con los
tiempos. La conversión base es más abstracta; nuestra intuición para tratar con valores de tiempo es mejor.
Pero si tenemos la perspicacia de tratar los tiempos como números de base 60 y hacemos la inversión
de escribir las funciones de conversión ( ), obtenemos uyn programa que es más corto, más fácil de leer
y depurar, y más confiable.
También es más fácil agregar funciones más adelante. Por ejemplo, imagina restar dos
Tiempos para encontrar la duración entre ellos. El enfoque ingenuo sería implementar la resta
con préstamo. Usar las funciones de conversión sería más fácil y más probable que sea correcto.
Irónicamente, a veces hacer un problema más difícil (o más general) lo hace más fácil
(porque hay menos casos especiales y menos oportunidades de error).
16.5 Depuración
Un objeto de tiempo está bien formado si los valores y y están entre 0 y 60
de (incluido 0 pero no 60) y si es positivo. pero deben ser valores enteros,
podríamos permitir tener una parte fraccionaria.
Requisitos como estos se llaman invariantes porque siempre deben ser verdaderos. Para
decirlo de otra manera, si no son ciertas, algo ha ido mal.
Escribir código para verificar invariantes puede ayudar a detectar errores y encontrar sus causas. Por
ejemplo, podría tener una función como la que toma un objeto de tiempo y lo devuelve si viola un
invariante:
Al comienzo de cada función, puede verificar los argumentos para asegurarse de que sean
válidos:
O podría usar una declaración de afirmación, que verifica un invariante dado y genera una
excepción si falla:
Las sentencias son útiles porque distinguen el código que se ocupa de las condiciones
normales del código que comprueba si hay errores.
Machine Translated by Google
160 Capítulo 16. Clases y funciones
16.6 Glosario
prototipo y parche: Un plan de desarrollo que implica escribir un borrador de un programa, probar y corregir
errores a medida que se encuentran.
Desarrollo diseñado: un plan de desarrollo que implica una visión de alto nivel del problema y más planificación
que el desarrollo incremental o el desarrollo de prototipos.
mento
función pura: Una función que no modifica ninguno de los objetos que recibe como argumentos.
La mayoría de las funciones puras son fructíferas.
modificador: una función que cambia uno o más de los objetos que recibe como argumentos. La mayoría de
los modificadores son nulos; es decir, vuelven .
estilo de programación funcional: un estilo de diseño de programa en el que la mayoría de func
ciones son puras.
invariante: Una condición que siempre debe ser cierta durante la ejecución de un programa.
declaración de aserción: una declaración que verifica una condición y genera una excepción si falla.
16.7 Ejercicios
Los ejemplos de código de este capítulo están disponibles en ; Las
soluciones de los ejercicios están disponibles en
.
Ejercicio 16.1. Escriba una función llamada que tome un objeto Tiempo y un número y devuelva un nuevo
objeto Tiempo que contenga el producto del Tiempo original y el número.
Luego use para escribir una función que tome un objeto de tiempo que represente el tiempo final en una
carrera y un número que represente la distancia, y devuelva un objeto de tiempo que represente el ritmo
promedio (tiempo por milla).
Ejercicio 16.2. El módulo proporciona objetos que son similares a los objetos de tiempo de este capítulo, pero
proporcionan un amplio conjunto de métodos y operadores. Lea la documentación en
.
1. Usa la módulo para escribir un programa que obtenga la fecha actual e imprima el día de
semana.
2. Escriba un programa que tome un cumpleaños como entrada e imprima la edad del usuario y el número
de días, horas, minutos y segundos hasta su próximo cumpleaños.
3. Para dos personas nacidas en días diferentes, hay un día en que una tiene el doble de edad que la otra.
Ese es su Día Doble. Escriba un programa que tome dos fechas de nacimiento y calcule su Día Doble.
4. Para un poco más de desafío, escriba la versión más general que calcula el día en que uno
persona es n veces mayor que la otra.
Solución:
Machine Translated by Google
capitulo 17
Clases y métodos
Aunque estamos usando algunas de las funciones orientadas a objetos de Python, los programas de los
últimos dos capítulos no están realmente orientados a objetos porque no representan las relaciones entre
los tipos definidos por el programador y las funciones que operan en ellos. El siguiente paso es transformar
esas funciones en métodos que hagan explícitas las relaciones.
Los ejemplos de código de este capítulo están disponibles en y
, las soluciones a los ejercicios están en
.
17.1 Funciones orientadas a objetos
Python es un lenguaje de programación orientado a objetos, lo que significa que proporciona funciones
que admiten la programación orientada a objetos, que tiene estas características definitorias:
• Los programas incluyen definiciones de clases y métodos.
• La mayor parte del cálculo se expresa en términos de operaciones sobre objetos.
• Los objetos a menudo representan cosas en el mundo real, y los métodos a menudo corresponden a las
formas en que interactúan las cosas en el mundo real.
Por ejemplo, la clase definida en el Capítulo 16 corresponde a la forma en que las personas registran la
hora del día, y las funciones que definimos corresponden al tipo de cosas que las personas hacen con las
horas. De manera similar, las clases y del Capítulo 15 corresponden a los conceptos matemáticos de un
punto y un rectángulo.
Hasta ahora, no hemos aprovechado las características que proporciona Python para admitir la
programación orientada a objetos. Estas características no son estrictamente necesarias; la mayoría de
ellos proporcionan una sintaxis alternativa para las cosas que ya hemos hecho. Pero en muchos casos, la
alternativa es más concisa y transmite con mayor precisión la estructura del programa.
Por ejemplo, en no hay una conexión obvia entre la definición de clase y
las definiciones de funciones que siguen. Con un poco de examen, es evidente que cada función toma al
menos un objeto como argumento.
Machine Translated by Google
162 Capítulo 17. Clases y métodos
Esta observación es la motivación de los métodos; un método es una función que está asociada con una clase
particular. Hemos visto métodos para cadenas, listas, diccionarios y tuplas. En este capítulo, definiremos métodos
para tipos definidos por el programador.
Los métodos son semánticamente lo mismo que las funciones, pero hay dos diferencias sintácticas:
• Los métodos se definen dentro de una definición de clase para hacer explícita la relación entre la clase y el
método.
• La sintaxis para llamar a un método es diferente de la sintaxis para llamar a una función.
En las próximas secciones, tomaremos las funciones de los dos capítulos anteriores y las transformaremos en
métodos. Esta transformación es puramente mecánica; puedes hacerlo siguiendo una secuencia de pasos. Si se
siente cómodo convirtiendo de un formulario a otro, podrá elegir el mejor formulario para lo que esté haciendo.
17.2 Objetos de impresión
En el Capítulo 16, definimos una clase llamada named y en la Sección 16.1, escribiste una función
:
Para llamar a esta función, debe pasar un objeto como argumento:
Para hacer un método, todo lo que tenemos que hacer es mover la definición de la función dentro del
definición de clase. Observe el cambio en la sangría.
Ahora hay dos formas de llamar a la . La primera (y menos común) forma es usar
sintaxis de la función:
La segunda forma (y más concisa) es usar la sintaxis del método:
Machine Translated by Google
17.3. Otro ejemplo 163
En este uso de la notación de puntos, es el objeto
es esl obre
el qdue
nombre el sme
invoca
étodo el método, que
(nuevamente), y se denomina
sujeto . Así como el sujeto de una oración es de lo que trata la oración, el sujeto de la invocación de
un método es de lo que trata el método.
Dentro del método, el sujeto se asigna al primer parámetro, por lo que en este caso se es
asigna a .
Por convención, el primer parámetro de un método se llama , asi seria mas comun
para escribir así:
La razón de esta convención es una metáfora implícita:
• La sintaxis para una llamada de , sugiere que la función es la ! Aquí
función, agente activo. Dice algo como, "Oye, hay un objeto para que usted
imprime".
• En la programación orientada a objetos, los objetos son los agentes activos. Un método invoca
ción como dice "Oye ! Por favor, imprímete”.
Este cambio de perspectiva puede ser más cortés, pero no es obvio que sea útil. En los ejemplos que
hemos visto hasta ahora, puede que no lo sea. Pero a veces cambiar la responsabilidad de las
funciones a los objetos hace posible escribir funciones (o métodos) más versátiles y facilita el
mantenimiento y la reutilización del código.
como ejercicio, reescribir (de la Sección 16.4) como método. Puede sentirse tentado
para reescribir también como un método, pero eso realmente no tiene sentido porque no habría ningún
objeto para invocarlo.
17.3 Otro ejemplo
Aquí hay una versión de (de la Sección 16.3) reescrito como un método:
Esta versión asume esa se escribe como un método. Además, tenga en cuenta que es un puro
función, no un modificador.
Así es como invocarías :
Machine Translated by Google
164 Capítulo 17. Clases y métodos
Este mecanismo puede resultar confuso, especialmente si comete un error. Por ejemplo, si invocas
con dos argumentos, se obtiene:
El mensaje de error es inicialmente confuso, porque solo hay dos argumentos entre paréntesis. Pero
el tema también se considera un argumento, así que en total son tres.
Por cierto, un argumento posicional es un argumento que no tiene un nombre de parámetro; es decir,
no es un argumento de palabra clave. En esta función llama:
y son posicionales y es un argumento de palabra clave.
17.4 Un ejemplo más complicado
Reescribir (de la Sección 16.1) es un poco más complicado porque toma dos objetos de tiempo como
parámetros. En este caso es convencional nombrar el primer parámetro y el segundo parámetro
:
Para usar este método, debe invocarlo en un objeto y pasar el otro como argumento:
Una cosa buena de esta sintaxis es que casi se lee como en inglés: "end is after start?"
17.5 El método de inicio
El método init (abreviatura de "inicialización") es un método especial que se invoca cuando se crea
una instancia de un objeto. Su nombre completo es (dos guiones bajos, seguidos de y luego dos
, guiones bajos más). Un método init para la clase podría verse como
este:
Es común que los parámetros de tener los mismos nombres que los atributos. El
declaración
Machine Translated by Google
almacena el valor del parámetro como atributo de .
Los parámetros son opcionales, así que si llamas sin argumentos, obtienes el valor predeterminado
a values.
Si proporciona un argumento, anula :
Si proporciona dos argumentos, estos anulan y .
Y si proporciona tres argumentos, anulan los tres valores predeterminados.
Como ejercicio, escriba un método init para la clase que tome y como parámetros opcionales y los
asigne a los atributos correspondientes.
17.6 El método
es un método especial, , que se supone que devuelve una cadena representa
como ción de un objeto.
Por ejemplo, aquí hay un método para objetos de tiempo:
Cuando escribo una nueva clase, casi siempre empiezo , lo que hace que sea más fácil
depuración. para instanciar objetos, y , escribiendo lo que es útil para la
Como ejercicio, escriba un método para la clase. Cree un objeto Punto e imprímalo.
17.7 Sobrecarga del operador
Al definir otros métodos especiales, puede especificar el comportamiento de los operadores en los
tipos definidos por el programador. Por ejemplo, si define un método con el nombre de la clase,
puede usar el operador en los objetos de tiempo.
Así es como se vería la definición:
Machine Translated by Google
166 Capítulo 17. Clases y métodos
Y así es como podrías usarlo:
Cuando aplica el operador a los objetos de tiempo, Python invoca . Cuando imprime el resultado, Python
invoca . ¡Así que están sucediendo muchas cosas detrás de escena!
Cambiar el comportamiento de un operador para que funcione con tipos definidos por el programador se
denomina sobrecarga de operadores. Para cada operador en Python hay un método especial
correspondiente, como . Para más detalles, consulte
.
Como ejercicio, escriba un método para la clase Point.
17.8 Despacho basado en tipos
En la sección anterior, agregamos dos objetos de tiempo, pero es posible que también desee agregar un
número entero a un objeto de tiempo. La siguiente es una versión de que comprueba el tipo de
e invoca ya sea o :
Si invoca. es
De lo
un contrario,
objeto asume que el parámetro es un número e invoca . Esta operación se denomina
de tiempo,
envío basado en tipos porque envía el cálculo a diferentes métodos según el tipo de argumentos.
Aquí hay ejemplos que usan el operador con diferentes tipos:
Machine Translated by Google
17.9. Polimorfismo 167
Desafortunadamente, esta implementación de la suma no es conmutativa. Si el entero es el primer
operando, obtienes
El problema es que, en lugar de pedirle al objeto Hora que agregue un número entero, Python le está
pidiendo a un número entero que agregue un objeto Hora y no sabe cómo hacerlo. Pero hay una
,
solución inteligente para este problema: el método especial que significa "suma del lado derecho". Este
método se invoca cuando aparece un objeto de tiempo en el lado derecho del operador. Aquí está la
definición:
Y así es como se usa:
Como ejercicio, escriba un método para Puntos que funcione con un objeto Punto o una tupla:
• Si el segundo operando es un Punto, el método debe devolver un nuevo Punto cuya coordenada
x sea la suma de las coordenadas x de los operandos, y lo mismo para las coordenadas y.
• Si el segundo operando es una tupla, el método debe agregar el primer elemento de la tupla a la
coordenada x y el segundo elemento a la coordenada y, y devolver un nuevo Punto con el
resultado.
17.9 Polimorfismo
El despacho basado en tipos es útil cuando es necesario, pero (afortunadamente) no siempre es
necesario. A menudo, puede evitarlo escribiendo funciones que funcionen correctamente para
argumentos con diferentes tipos.
Muchas de las funciones que escribimos para cadenas también funcionan para otros tipos de secuencias. Por
ejemplo, en la Sección 11.2 solíamos contar el número de veces que aparece cada letra en una palabra.
Machine Translated by Google
168 Capítulo 17. Clases y métodos
Esta función también funciona para listas, tuplas e incluso diccionarios, siempre que los elementos de sean
hashable, para que puedan usarse como claves en .
Las funciones que trabajan con varios tipos se llaman polimórficas. El polimorfismo puede facilitar la
, los elementos de una secuencia,
reutilización del código. Por ejemplo, la función incorporada que agrega
funciona siempre que los elementos de la secuencia admitan la adición.
Dado que los objetos de tiempo proporcionan una método, trabajan con:
En general, si todas las operaciones dentro de una función funcionan con un tipo dado, la función funciona
con ese tipo.
El mejor tipo de polimorfismo es el tipo no intencional, donde descubres que una función que ya escribiste
se puede aplicar a un tipo que nunca planeaste.
17.10 Depuración
Es legal agregar atributos a los objetos en cualquier punto de la ejecución de un programa, pero si tiene
objetos del mismo tipo que no tienen los mismos atributos, es fácil cometer errores. Se considera una
buena idea inicializar todos los atributos de un objeto en el método init.
Si no está seguro de si un objeto tiene un atributo en particular, puede usar la función incorporada
(consulte la Sección 15.7).
Otra forma de acceder a los atributos es que la función integrada , que toma un objeto y
devuelve un diccionario que asigna los nombres de los atributos (como cadenas) a sus valores:
Para fines de depuración, puede que le resulte útil tener esta función a mano:
atraviesa el diccionario e imprime cada nombre de atributo y su correlato
valor correspondiente.
La función incorporada el toma un objeto y un nombre de atributo (como una cadena) y devuelve
valor del atributo.
Machine Translated by Google
17.11. Interfaz e implementación 169
17.11 Interfaz e implementación
Uno de los objetivos del diseño orientado a objetos es hacer que el software sea más fácil de mantener, lo
que significa que puede mantener el programa funcionando cuando otras partes del sistema cambian y
modificar el programa para cumplir con los nuevos requisitos.
Un principio de diseño que ayuda a lograr ese objetivo es mantener las interfaces separadas de las
implementaciones. Para los objetos, eso significa que los métodos que proporciona una clase no deberían
depender de cómo se representen los atributos.
Por ejemplo, en este capítulo desarrollamos una clase que representa una hora del día. Los métodos
proporcionados por esta clase incluyen , ,y .
Podríamos implementar esos métodos de varias maneras. Los detalles de la implementación
dependen de cómo representemos el tiempo. En este capítulo, los atributos de un objeto son ,
,y .
Como alternativa, podríamos reemplazar estos atributos con un solo número entero que represente la
cantidad de segundos desde la medianoche. Esta implementación haría que algunos métodos fueran más
, fáciles de escribir, pero hace que otros métodos sean más difíciles.
Después de implementar una nueva clase, es posible que descubra una mejor implementación. Si
otras partes del programa están usando su clase, puede llevar mucho tiempo y ser propenso a errores
cambiar la interfaz.
Pero si diseñó la interfaz con cuidado, puede cambiar la implementación sin cambiar la
interfaz, lo que significa que otras partes del programa no tienen que cambiar.
17.12 Glosario
lenguaje orientado a objetos: un lenguaje que proporciona funciones, como tipos y métodos
definidos por el programador, que facilitan la programación orientada a objetos.
Programación orientada a objetos: un estilo de programación en el que los datos y las operaciones
que lo manipulan están organizados en clases y métodos.
método: una función que se define dentro de una definición de clase y se invoca en instancias de
esa clase.
sujeto: El objeto sobre el que se invoca un método.
argumento posicional: un argumento que no incluye un nombre de parámetro, por lo que no es un
argumento de palabra clave.
sobrecarga del operador: cambiar el comportamiento de un operador para que funcione con un
tipo definido por el programador.
despacho basado en tipo: Un patrón de programación que verifica el tipo de un operando y en
invoca diferentes funciones para diferentes tipos.
polimórfico: Perteneciente a una función que puede trabajar con más de un tipo.
Machine Translated by Google
170 Capítulo 17. Clases y métodos
17.13 Ejercicios
Ejercicio 17.1. Descargue el código de este capítulo para que sea un
. Cambia los atributos de la solo número entero que represente los segundos desde
noche. Luego modifique los métodos (y la función mediados de) para trabajar con el nuevo
tation. No debería tener que modificar el código de prueba implemento. Cuando haya terminado, la salida debería
para que sea el mismo que antes. .
Solución: Ejercicio 17.2. Este ejercicio es una advertencia sobre uno de los errores más comunes y difíciles
de encontrar. en Python Escriba una definición para una clase nombrada con los siguientes métodos:
2. Un método llamado que toma un objeto de cualquier tipo y lo agrega a
.
método que devuelve una representación de cadena del objeto Canguro y el
contenido de la bolsa.
Pruebe su código creando dos objetos, asignándolos a variables nombradas y luego y
, agregándolos al contenido de la bolsa de .
Descargue el . Contiene una solución a la
problema anterior con un error grande y desagradable. Encuentra y corrige el error.
Si se atasca, puede descargarlo, ,
que explica el problema y demuestra una solución.
Machine Translated by Google
capitulo 18
Herencia
La característica del lenguaje más frecuentemente asociada con la programación orientada a objetos
es la herencia. La herencia es la capacidad de definir una nueva clase que es una versión modificada
de una clase existente. En este capítulo, demuestro la herencia usando clases que representan
naipes, barajas de cartas y manos de póquer.
Si no juegas al póquer, puedes leer sobre ello en ,
pero no tienes que hacerlo; Te diré lo que necesitas saber para los ejercicios.
Los ejemplos de código de este capítulo están disponibles en
.
18.1 Objetos de tarjeta
Hay cincuenta y dos cartas en una baraja, cada una de las cuales pertenece a uno de cuatro
palos y uno de trece rangos. Los palos son Picas, Corazones, Diamantes y Tréboles (en
orden descendente en el bridge). Los rangos son As, 2, 3, 4, 5, 6, 7, 8, 9, 10, Jota, Reina y
Rey. Según el juego que esté jugando, un As puede ser mayor que el Rey o menor que 2.
Si queremos definir un nuevo objeto para representar un naipe, es obvio cuáles deben ser los
atributos: y . No es tan obvio de qué tipo deben ser los atributos.
Una posibilidad es usar cadenas que contengan palabras como trajes y rangos. Un problema
con esta implementación es que no sería fácil comparar cartas para ver cuál tenía un rango o
palo más alto.
Una alternativa es usar números enteros para codificar los rangos y palos. En este contexto,
“codificar” significa que vamos a definir un mapeo entre números y palos, o entre números y
rangos. Este tipo de codificación no pretende ser un secreto (eso sería "cifrado").
Por ejemplo, esta tabla muestra los palos y los códigos enteros correspondientes:
Picas → 3
Corazones → 2
Diamantes → 1
Clubes → 0
Machine Translated by Google
172 Capítulo 18. Herencia
Este código facilita la comparación de tarjetas; Debido a que los palos más altos se asignan a números más
altos, podemos comparar palos comparando sus códigos.
El mapeo de rangos es bastante obvio; cada uno de los rangos numéricos se asigna al entero
correspondiente, y para las cartas con figuras:
jota → 11
Reina → 12
Rey → 13
Estoy usando el símbolo → para dejar en claro que estas asignaciones no son parte del
programa Python. Son parte del diseño del programa, pero no aparecen explícitamente en el código.
La definición de clase para Se ve como esto:
Como de costumbre, el método init toma un parámetro opcional para cada atributo. La carta por defecto
es el 2 de Tréboles.
Para crear una Tarjeta, llamas con el palo y el rango de la carta que quieras.
18.2 Atributos de clase
Para imprimir objetos de cartas de una manera que las personas puedan leer fácilmente, necesitamos
un mapeo de los códigos enteros a los rangos y palos correspondientes. Una forma natural de hacerlo
es con listas de cadenas. Asignamos estas listas a atributos de clase:
Las variables como las que se dyefinen dentro de una
, clase pero fuera de cualquier método, se
denominan atributos de clase porque están asociadas con el objeto de clase.
.
Este término las distingue de variables como y , que se llaman instancia
atributos porque están asociadas con una instancia particular.
Se accede a ambos tipos de atributos mediante la notación de puntos. Por ejemplo, ,
in es un objeto Card y es su rango. De manera similar, es un objeto de clase y es una lista de cadenas
asociadas con la clase.
Machine Translated by Google
18.3. Comparando cartas 173
tipo lista
Tarjeta trajes_nombres
lista
rank_names
Tarjeta
tarjeta1 traje 1
rango 11
Figura 18.1: Diagrama de objetos.
Cada tarjeta tiene la suya. y , pero solo hay una copia de y
.
Poniendo todo junto, la expresión significa "usar el at de
tributo del objeto como un índice en la lista y seleccione la clase ,
la cadena apropiada”.
El primer elemento de porque no hay ces
arta con rango cero. Al incluirlo como guardián de lugar, obtenemos
una asignación con la buena propiedad de que el índice 2 se asigna a la cadena y así
, evitar este ajuste, podríamos haber usado un diccionario en lugar de una lista.
sucesivamente. Para
Con los métodos que tenemos hasta ahora, podemos crear e imprimir tarjetas:
La figura 18.1 es un diagrama del objeto de clase y una instancia de Card. es un objeto de clase; su tipo es
una instancia de por lo que .su tipo es . Para ahorrar espacio, yo
,y
no dibujó el contenido de .
18.3 Comparación de cartas
Para los tipos integrados, existen operadores relacionales (, , , etc.) que comparan valores y
determinan cuándo uno es mayor, menor o igual que otro. Para los tipos definidos por el
programador, podemos anular el comportamiento de los operadores integrados al proporcionar un
, método denominado que significa "menor que".
El orden correcto de las tarjetas no es obvio. Por ejemplo, ¿cuál es mejor, el 3 de
Tréboles o el 2 de Diamantes? Uno tiene un rango más alto, pero el otro tiene un palo
más alto. Para comparar cartas, debe decidir si el rango o el palo es más importante.
La respuesta puede depender del juego que esté jugando, pero para simplificar las cosas, tomaremos la
decisión arbitraria de que el palo es más importante, de modo que todas las espadas superen a todas las
diamantes, y así sucesivamente.
Machine Translated by Google
174 Capítulo 18. Herencia
Con eso decidido, podemos escribir :
Puedes escribir esto de manera más concisa usando la comparación de tuplas:
Como ejercicio, escriba un método para objetos de tiempo. Puede usar la comparación de tuplas, pero
también podría considerar comparar números enteros.
18.4 Mazos
Ahora que tenemos Cartas, el siguiente paso es definir Barajas. Dado que un mazo se compone de
cartas, es natural que cada Deck contenga una lista de cartas como atributo.
La siguiente es una definición de clase para . El método init crea el atributo. y
genera el conjunto estándar de cincuenta y dos cartas:
La forma más fácil de llenar el mazo es con un bucle anidado. El bucle exterior enumera los palos del
0 al 3. El bucle interior enumera los rangos del 1 al 13. Cada iteración crea una nueva Carta con el
palo y el rango actuales, y la agrega a .
18.5 Impresión de la baraja
Aquí hay un método para :
Machine Translated by Google
18.6. Añadir, eliminar, barajar y ordenar 175
Este método demuestra una forma eficiente de acumular una cadena grande: crear una lista de cadenas y luego usar
el método de cadena. La función integrada invoca el método en cada tarjeta y devuelve la representación de cadena.
Ya que invocamos en un carácter de nueva línea, las tarjetas están separadas por nuevas líneas. Aquí está
cómo se ve el resultado:
Aunque el resultado aparece en 52 líneas, es una cadena larga que contiene saltos de línea.
18.6 Añadir, eliminar, barajar y ordenar
Para repartir cartas, nos gustaría un método que retire una carta de la baraja y la devuelva.
El método de lista proporciona una manera conveniente de hacerlo:
Dado que elimina la última carta de la lista, estamos repartiendo desde la parte inferior de la baraja.
Para agregar una tarjeta, podemos usar el método de lista :
Un método como este que usa otro método sin hacer mucho trabajo a veces se llama revestimiento. La metáfora
proviene de la carpintería, donde una chapa es una capa delgada de madera de buena calidad pegada a la superficie
de una pieza de madera más barata para mejorar la apariencia.
En este caso es un método “delgado” que expresa una operación de lista en términos apropiados para mazos. Mejora
la apariencia, o interfaz, de la implementación.
Como otro ejemplo, podemos escribir un método Deck nombrado desde el usando la función
módulo:
Machine Translated by Google
176 Capítulo 18. Herencia
No olvides importar .
18.7 Herencia
La herencia es la capacidad de definir una nueva clase que es una versión modificada de una clase existente.
Como ejemplo, digamos que queremos que una clase represente una “mano”, es decir, las cartas que
tiene un jugador. Una mano es similar a una baraja: ambas se componen de una colección de cartas y
ambas requieren operaciones como agregar y quitar cartas.
Una mano también es diferente de una baraja; hay operaciones que queremos para manos que no tienen sentido
para una baraja. Por ejemplo, en el póquer podemos comparar dos manos para ver cuál gana. En el bridge,
podríamos calcular el puntaje de una mano para hacer una oferta.
Esta relación entre clases —similares, pero diferentes— se presta a la herencia. Para definir una nueva clase que
herede de una clase existente, coloque el nombre de la clase existente entre paréntesis:
Cuando una nueva clase hereda de una existente, la existente se llama padre y la nueva clase se llama hijo.
,
En este ejemplo, hereda de pero en realidad no hace lo que queremos:
en lugar de llenar la mano con 52 cartas
nuevas, el método init para Hands debería inicializarse con una lista vacía.
Cuando crea una mano, Python invoca este método init, no el que está en .
18.8. diagramas de clase 177
Un próximo paso natural es encapsular este código en un método llamado :
toma dos argumentos, un objeto Mano y el número de cartas a repartir. Se modifica
cumple con ambos y , y regresa .
En algunos juegos, las cartas se mueven de una mano a otra, o de una mano al mazo. Puedes
usar para cualquiera de estas operaciones: puede ser un Deck o una Mano, y , a pesar del
nombre, también puede ser un .
La herencia es una característica útil. Algunos programas que serían repetitivos sin la herencia se
pueden escribir de manera más elegante con ella. La herencia puede facilitar la reutilización del
código, ya que puede personalizar el comportamiento de las clases principales sin tener que
modificarlas. En algunos casos, la estructura de herencia refleja la estructura natural del problema,
lo que facilita la comprensión del diseño.
Por otro lado, la herencia puede dificultar la lectura de los programas. Cuando se invoca un
método, a veces no está claro dónde encontrar su definición. El código relevante puede
distribuirse en varios módulos. Además, muchas de las cosas que se pueden hacer usando
la herencia se pueden hacer igual o mejor sin ella.
18.8 Diagramas de clases
Hasta ahora hemos visto diagramas de pila, que muestran el estado de un programa, y diagramas de
objetos, que muestran los atributos de un objeto y sus valores. Estos diagramas representan una
instantánea de la ejecución de un programa, por lo que cambian a medida que se ejecuta el programa.
También son muy detallados; para algunos propósitos, demasiado detallada. Un diagrama de
clases es una representación más abstracta de la estructura de un programa. En lugar de
mostrar objetos individuales, muestra clases y las relaciones entre ellos.
Hay varios tipos de relaciones entre clases:
• Los objetos de una clase pueden contener referencias a objetos de otra clase. Por ejemplo,
cada Rectángulo contiene una referencia a un Punto y cada Mazo contiene referencias a
muchas Cartas. Este tipo de relación se llama HASA, como en "un rectángulo tiene un
punto".
• Una clase puede heredar de otra. Esta relación se llama ISA, como en "una mano es
una especie de mazo".
• Una clase puede depender de otra en el sentido de que los objetos de una clase toman
objetos de la segunda clase como parámetros, o usan objetos de la segunda clase
como parte de un cálculo. Este tipo de relación se llama dependencia.
Un diagrama de clases es una representación gráfica de estas relaciones. Por ejemplo,
la figura 18.2 muestra las relaciones entre , y .
Machine Translated by Google
178 Capítulo 18. Herencia
Cubierta
* Tarjeta
Mano
Figura 18.2: Diagrama de clases.
La flecha con cabeza triangular hueca representa una relación ISA; en este caso indica que Hand
hereda de Deck.
La punta de flecha estándar representa una relación HASA; en este caso, una baraja tiene
referencias a objetos de cartas.
La estrella () cerca de la punta de la flecha es una multiplicidad; indica cuántas Cartas tiene un Deck.
Una multiplicidad puede ser un simple número, como o u,na
erstrella,
un ango, clomo
o que indica que un Deck puede tener
cualquier número de Cartas.
No hay dependencias en este diagrama. Normalmente se mostrarían con una flecha discontinua. O
si hay muchas dependencias, a veces se omiten.
Un diagrama más detallado podría mostrar que un Deck en realidad contiene una lista de Cartas, pero los
tipos incorporados como list y dict generalmente no se incluyen en los diagramas de clase.
18.9 Depuración
La herencia puede dificultar la depuración porque cuando invoca un método en un objeto, puede ser
difícil determinar qué método se invocará.
Suponga que está escribiendo una función que funciona con objetos de mano. Le gustaría que
funcionara con todo tipo de manos, como PokerHands, BridgeHands, etc. Si invoca un método
como , puede obtener el definido en pero si alguna de las s,ubclases anula este método, obtendrá
esa versión en su lugar. . Este comportamiento suele ser bueno, pero puede ser confuso.
Cada vez que no esté seguro del flujo de ejecución a través de su programa, la solución más simple
es agregar sentencias de impresión al comienzo de los métodos relevantes. Si imprime un mensaje
que dice algo como entonces, mientras se ejecuta el programa, rastrea el f,lujo de
ejecución.
Como alternativa, podría usar esta función, que toma un objeto y un nombre de método (como una
cadena) y devuelve la clase que proporciona la definición del método:
Aquí hay un ejemplo:
Machine Translated by Google
18.10. Encapsulación de datos 179
Entonces el método para esta Mano es el que está en .
utiliza el método para obtener la lista de objetos de clase (tipos) en los que
se buscarán métodos. "MRO" significa "orden de resolución de métodos", que es la secuencia de
clases que Python busca para "resolver" el nombre de un método.
Aquí hay una sugerencia de diseño: cuando reemplaza un método, la interfaz del nuevo método debe
ser la misma que la anterior. Debe tomar los mismos parámetros, devolver el mismo tipo y obedecer
las mismas condiciones previas y posteriores. Si sigue esta regla, encontrará que cualquier función
diseñada para funcionar con una instancia de una clase principal, como una baraja, también funcionará
con instancias de clases secundarias como una mano y una mano de póquer.
Si viola esta regla, que se llama el "principio de sustitución de Liskov", su código colapsará como (lo
siento) un castillo de naipes.
18.10 Encapsulación de datos
Los capítulos anteriores demuestran un plan de desarrollo que podríamos llamar "diseño orientado a
objetos". Identificamos los objetos que necesitábamos, , y —y definido
como clases para representarlos. En cada caso hay una correspondencia obvia entre el objeto y
alguna entidad en el mundo real (o al menos un mundo matemático).
Pero a veces es menos obvio qué objetos necesita y cómo deben interactuar. En ese caso se necesita
un plan de desarrollo diferente. De la misma manera que descubrimos las interfaces de función por
encapsulación y generalización, podemos descubrir las interfaces de clase por encapsulación de datos.
El análisis de Markov, de la Sección 13.8, proporciona un buen ejemplo. Si descarga mi código de ,
verá que utiliza dos variables globales, que se leen y escriben desde varias funciones.
y
Debido a que estas variables son globales, solo podemos ejecutar un análisis a la vez. Si leemos
dos textos, sus prefijos y sufijos se agregarían a las mismas estructuras de datos (lo que hace
que el texto generado sea interesante).
Para ejecutar varios análisis y mantenerlos separados, podemos encapsular el estado de cada análisis
en un objeto. Esto es lo que parece:
A continuación, transformamos las funciones en métodos. Por ejemplo, aquí está :
Machine Translated by Google
180 Capítulo 18. Herencia
Transformar un programa como este (cambiar el diseño sin cambiar el comportamiento) es otro ejemplo de
refactorización (consulte la Sección 4.7).
Este ejemplo sugiere un plan de desarrollo para diseñar objetos y métodos:
1. Comience escribiendo funciones que lean y escriban variables globales (cuando sea necesario).
2. Una vez que tenga el programa funcionando, busque asociaciones entre variables globales
y las funciones que los utilizan.
3. Encapsular variables relacionadas como atributos de un objeto.
4. Transformar las funciones asociadas en métodos de la nueva clase.
Como ejercicio, descargue mi código de Markov y siga los pasos
, descritos anteriormente para encapsular las variables globales. Solución:
como atributos de una nueva clase llamada
.
18.11 Glosario
codificar: para representar un conjunto de valores utilizando otro conjunto de valores mediante la construcción de un mapa
hacer ping entre ellos.
atributo de clase: Un atributo asociado con un objeto de clase. Los atributos de clase están definidos
dentro de una definición de clase pero fuera de cualquier método.
atributo de instancia: Un atributo asociado con una instancia de una clase.
barniz: Un método o función que proporciona una interfaz diferente a otra función con
haciendo muchos cálculos.
herencia: La capacidad de definir una nueva clase que es una versión modificada de una previamente
clase definida.
clase principal: la clase de la que hereda una clase secundaria.
clase secundaria: una nueva clase creada al heredar de una clase existente; también llamado "sub
clase".
Relación ISA: una relación entre una clase secundaria y su clase principal.
Relación HASA: Una relación entre dos clases donde las instancias de una clase contienen referencias a
instancias de la otra.
dependencia: una relación entre dos clases donde las instancias de una clase se usan en instancias de la otra
clase, pero no las almacenan como atributos.
Machine Translated by Google
18.12. Ejercicios 181
diagrama de clases: un diagrama que muestra las clases en un programa y las relaciones
entre ellos.
multiplicidad: una notación en un diagrama de clases que muestra, para una relación HASA, cuántas
referencias hay a instancias de otra clase.
encapsulación de datos: un plan de desarrollo de programa que implica un prototipo que utiliza
variables globales y una versión final que convierte las variables globales en atributos de instancia.
18.12 Ejercicios
Ejercicio 18.1. Para el siguiente programa, dibuje un diagrama de clases UML que muestre estas
clases y las relaciones entre ellas.
Ejercicio 18.2. Escribe un método Deck llamado que tome dos parámetros, el número de manos y el
número de cartas por mano. Debe crear la cantidad adecuada de objetos Mano, repartir la cantidad
adecuada de cartas por mano y devolver una lista de Manos.
Ejercicio 18.3. Las siguientes son las manos posibles en el póquer, en orden creciente de valor y
orden decreciente de probabilidad:
pareja: dos cartas del mismo valor
dos parejas: dos parejas de cartas del mismo valor
trío: tres cartas del mismo valor
escalera: cinco cartas con rangos en secuencia (los ases pueden ser altos o bajos, por lo que es un recto
y tambien , pero no es.)
color: cinco cartas del mismo palo
full house: tres cartas con un rango, dos cartas con otro
Machine Translated by Google
182 Capítulo 18. Herencia
póquer: cuatro cartas del mismo valor escalera
de color: cinco cartas en secuencia (como se definió anteriormente) y del mismo palo
El objetivo de estos ejercicios es estimar la probabilidad de dibujar estas distintas manos.
1. Descargue los siguientes archivos de: :
Una versión completa de: , y clases de este capítulo.
Una implementación incompleta de una clase que representa una mano de póquer
y algún código que la prueba.
, de póquer de 7 cartas y verifica si alguna de ellas contiene color. Lea
2. Si lo ejecuta, reparte siete manos
este código detenidamente antes de continuar.
4. Escriba un método denominado que determine la clasificación de mayor valor para una mano y establezca el
atributo en consecuencia. Por ejemplo, una mano de 7 cartas puede contener un color y un par; debe estar
etiquetado como "al ras".
5. Cuando esté convencido de que sus métodos de clasificación funcionan, el siguiente paso es
estimar las probabilidades de las distintas manos. Escriba una función en la que baraje una baraja
de cartas, la divida en manos, clasifique las manos y cuente el número de veces que aparecen
varias clasificaciones.
6. Imprime una tabla de las clasificaciones y sus probabilidades. Ejecute su programa con un número
cada vez mayor de manos hasta que los valores de salida converjan en un grado razonable de
precisión. Compare sus resultados con los valores de
.
Solución: .
Machine Translated by Google
capitulo 19
las golosinas
Uno de mis objetivos para este libro ha sido enseñarle lo menos posible sobre Python. Cuando había
dos formas de hacer algo, elegía una y evitaba mencionar la otra. O a veces pongo el segundo en un
ejercicio.
Ahora quiero regresar por algunas de las partes buenas que quedaron atrás. Python proporciona una
serie de características que no son realmente necesarias (puedes escribir buen código sin ellas), pero
con ellas a veces puedes escribir código que es más conciso, legible o eficiente, y
a veces los tres.
19.1 Expresiones condicionales
Vimos sentencias condicionales en la Sección 5.4. Las declaraciones condicionales se utilizan a menudo
para elegir uno de dos valores; Por ejemplo:
Esta sentencia comprueba si es positiva. Si es así, calcula generaría un . Si no,
ValueError. Para evitar detener el programa, generamos un "NaN", que es un valor especial de punto
flotante que representa "No es un número".
Podemos escribir esta declaración de manera más concisa usando una expresión condicional:
Casi puedes leer esta línea como en inglés: "gets obtiene log si es mayor que 0; de lo contrario
NaN".
Las funciones recursivas a veces se pueden reescribir usando expresiones condicionales. Por ejemplo,
aquí hay una versión recursiva de :
Machine Translated by Google
184 Capítulo 19. Las golosinas
Podemos reescribirlo así:
Otro uso de las expresiones condicionales es el manejo de argumentos opcionales. Por ejemplo, aquí está el método
init de (ver Ejercicio 17.2):
Podemos reescribir este así:
En general, puede reemplazar una declaración condicional con una expresión condicional si ambas ramas contienen
expresiones simples que se devuelven o se asignan a la misma variable.
19.2 Lista de comprensiones
En la Sección 10.7 vimos el mapa y los patrones de filtro. Por ejemplo, esta función toma una lista de cadenas, asigna
el método de cadena a los elementos y devuelve una nueva lista de cadenas:
Podemos escribir esto de manera más concisa usando una lista de comprensión:
Los operadores de paréntesis indican que estamos construyendo una nueva lista. La expresión dentro de los corchetes
especifica los elementos de la lista, y la cláusula indica qué secuencia estamos atravesando.
La sintaxis de una lista por comprensión es un poco incómoda porque la variable de ciclo, en este ejemplo, aparece
en la expresión antes de que lleguemos a la definición.
Las listas de comprensión también se pueden usar para filtrar. Por ejemplo, esta función selecciona solo los elementos
que están en mayúsculas y devuelve una nueva lista:
Machine Translated by Google
19.3. Generador de expresiones 185
Podemos reescribirlo usando una lista por comprensión.
Las listas de comprensión son concisas y fáciles de leer, al menos para expresiones simples. Y
suelen ser más rápidos que el equivalente de bucles, a veces mucho más rápidos. Entonces, si
estás enojado conmigo por no mencionarlos antes, lo entiendo.
Pero, en mi defensa, las listas por comprensión son más difíciles de depurar porque no se puede
poner una declaración de impresión dentro del bucle. Le sugiero que los use solo si el cálculo es lo
suficientemente simple como para hacerlo bien la primera vez. Y para los principiantes eso significa nunca.
19.3 Expresiones generadoras
Las expresiones generadoras son similares a las listas de comprensión, pero con paréntesis en lugar
de corchetes:
El resultado es un objeto generador que sabe cómo iterar a través de una secuencia de valores. Pero
a diferencia de una lista de comprensión, no calcula todos los valores a la vez; espera a ser preguntado.
La función incorporada obtiene el siguiente valor del generador:
Cuando llega al final de la secuencia, genera una excepción StopIteration. También puede usar un
bucle para iterar a través de los valores:
El objeto generador realiza un seguimiento de dónde se encuentra en la secuencia, bucle recoge
así que dónde se quedó. Una vez que el generador se agota, continúa subiendo :
Las expresiones generadoras se usan a menudo con funciones como , , y :
19.4 y
Python proporciona una función integrada, , que toma una secuencia de valores booleanos y re .
son turnos si alguno de los valores Funciona en listas:
Machine Translated by Google
186 Capítulo 19. Las golosinas
Pero a menudo se usa con expresiones generadoras:
Ese ejemplo no es muy útil porque hace lo mismo que el operador. Pero podríamos reescribir algunas de
las funciones de búsqueda que escribimos en la Sección 9.3. Por ejemplo, podríamos escribir así:
Usar con una expresión generadora es eficiente porque se detiene inmediatamente si encuentra un
valor, por lo que no tiene que evaluar toda la secuencia.
,la secuencia
Python proporciona otra función integrada, que devuelve si cada elemento de la
es . Como ejercicio, utilice para reescribir Sección 9.3.
19,5 conjuntos
En la Sección 13.6 utilizo diccionarios para encontrar las palabras que aparecen en un documento pero no
en una lista de palabras. La función que escribí ,toma la que contiene las palabras del documento como
, contiene la lista de palabras. Devuelve un diccionario que contiene las claves que no están
claves y la que
en .
En todos estos diccionarios, los valores desperdician porque nunca los usamos. Como resultado, nosotros
algo de espacio de almacenamiento.
,
Python proporciona otro tipo integrado, llamado a, que se comporta como una colección de claves de
diccionario sin valores. Agregar elementos a un conjunto es rápido; también lo es verificar la membresía.
Y los conjuntos proporcionan métodos y operadores para calcular operaciones de conjuntos comunes.
Por ejemplo, la resta de conjuntos está disponible como un método llamado . o como operador,
Entonces podemos reescribir como esto:
El resultado es un conjunto en lugar de un diccionario, pero para operaciones como la iteración, el comportamiento
es el mismo.
Algunos de los ejercicios de este libro se pueden hacer de manera concisa y eficiente con series. Por
ejemplo, aquí hay una solución del Ejercicio 10.7, que u,sa un diccionario:
Machine Translated by Google
19.6. Contadores 187
Cuando un elemento aparece por primera vez, se agrega al diccionario. Si vuelve a aparecer el mismo elemento, la
función devuelve .
Usando conjuntos, podemos escribir la misma función así:
Un elemento solo puede aparecer una vez en un conjunto, por lo que si un elemento aparece más de una vez, el
conjunto será más pequeño que . Si no hay duplicados, el conjunto tendrá el mismo tamaño que .
También podemos usar conjuntos para hacer algunos de los ejercicios del Capítulo 9. Por ejemplo, aquí hay una
versión con un bucle:
El operador verifica si un conjunto es un subconjunto de otro, incluida la posibilidad de que sean iguales, lo cual es
cierto si todas las letras en aparecer en .
Como ejercicio, reescribe utilizando conjuntos.
19.6 Contadores
Un Contador es como un conjunto, excepto que si un elemento aparece más de una vez, el Contador realiza un
seguimiento de cuántas veces aparece. Si está familiarizado con la idea matemática de un conjunto múltiple, un
Contador es una forma natural de representar un conjunto múltiple.
El contador se define en un módulo estándar llamado puede , así que tienes que importarlo. Tú
inicializar un contador con una cadena, una lista o cualquier otra cosa que admita la iteración:
Los contadores se comportan como diccionarios en muchos aspectos; se asignan desde cada tecla al número de
veces que aparece. Como en los diccionarios, las claves tienen que ser hashable.
A diferencia de los diccionarios, los contadores no generan una excepción si accede a un elemento que no aparece.
En su lugar, devuelven 0:
Machine Translated by Google
188 Capítulo 19. Las golosinas
Podemos usar contadores para reescribir del ejercicio 10.6:
Si dos palabras son anagramas, contienen las mismas letras con los mismos números, por lo que sus
Contadores son equivalentes.
Los contadores proporcionan métodos y operadores para realizar operaciones similares a conjuntos,
incluidas la suma, la resta, la unión y la intersección. Y proporcionan un método a menudo útil, que
, devuelve una lista de pares de valorfrecuencia, ordenados del más común al más común.
el menos:
19.7 dictamen predeterminado
El módulo también proporciona si accede a , que es como un diccionario excepto que
una clave que no existe, puede generar un nuevo valor sobre la marcha.
Cuando crea un dictado predeterminado, proporciona una función que se usa para crear nuevos
valores. Una función utilizada para crear objetos a veces se denomina fábrica. Las funciones integradas
que crean listas, conjuntos y otros tipos se pueden usar como fábricas:
, cuál es un objeto de clase, no
Tenga en cuenta que el argumento es , que es una nueva lista.
La función que proporciona no se llama a menos que acceda a una clave que no existe.
La nueva lista, a la que llamamos , también se agrega al diccionario. Entonces si modificamos,
el
cambio aparece en:
Si está haciendo un diccionario de listas, a menudo puede escribir un código más .
simple usando En mi solución al Ejercicio 12.2, que puede
, obtener de Hago un diccionario que asigna una cadena ordenada de letras a la
palabras que se pueden escribir con esas letras. Por ejemplo, lista de mapas a la lista
.
Aquí está el código original:
Machine Translated by Google
19.8. tuplas con nombre 189
Esto se puede simplificar usando , que podría haber usado en el ejercicio 11.2:
Esta solución tiene el inconveniente de que crea una nueva lista cada vez, independientemente de si es
necesario. Para las listas, eso no es gran cosa, pero si la función de fábrica es complicada, podría serlo.
Podemos evitar este problema y simplificar el código usando un :
Mi solución al Ejercicio 18.3, que puedes descargar desde
, usos en la función . esta solución
ción tiene el inconveniente de crear una objeto cada vez que pasa por el bucle, ya sea
necesaria o no. Como ejercicio, reescríbalo usando un dictado predeterminado.
19.8 Tuplas con nombre
Muchos objetos simples son básicamente colecciones de valores relacionados. Por ejemplo, el objeto
Punto definido en el Capítulo 15 contiene dos números y . Cuando define una clase como esta,
generalmente comienza con un método init y un método str:
Machine Translated by Google
190 Capítulo 19. Las golosinas
Esto es mucho código para transmitir una pequeña cantidad de información. Python proporciona una
forma más concisa de decir lo mismo:
El primer argumento es el nombre de la clase que desea crear. El segundo es una lista de los atributos
que deben tener los objetos Point, como cadenas. El valor de retorno de es un objeto de clase:
proporciona automáticamente métodos y para que no tengas que hacerlo
como escribirlos.
Para crear un objeto Point, usa la clase Point como una función:
El método init asigna los argumentos a los atributos usando los nombres que proporcionó. El método str
imprime una representación del objeto Point y sus atributos.
Puede acceder a los elementos de la tupla nombrada por su nombre:
Pero también puede tratar una tupla con nombre como una tupla:
Las tuplas con nombre proporcionan una forma rápida de definir clases simples. El inconveniente es que
las clases simples no siempre se mantienen simples. Puede decidir más tarde que desea agregar métodos
a una tupla con nombre. En ese caso, podría definir una nueva clase que herede de la tupla nombrada:
O podría cambiar a una definición de clase convencional.
19.9 Recopilación de argumentos de palabras clave
En la Sección 12.4, vimos cómo escribir una función que reúne sus argumentos en una tupla:
Puede llamar a esta función con cualquier cantidad de argumentos posicionales (es decir, argumentos que
no tienen palabras clave):
Machine Translated by Google
19.10. Glosario 191
Pero el operador no recopila argumentos de palabras clave:
Para recopilar argumentos de palabras clave, puede usar el operador:
Puede llamar al parámetro de recopilación de palabras clave como desee, pero es un común
elija. El resultado es un diccionario que mapea de palabras clave a valores:
Si tiene un diccionario de palabras clave y valores, puede usar el operador de dispersión, llamar a a
una función:
Sin el operador de dispersión, la función se trataría como un solo argumento posicional, por lo que se
asignaría y se quejaría porque no hay nada que asignar:
Cuando trabaja con funciones que tienen una gran cantidad de parámetros, a menudo es útil crear y pasar
diccionarios que especifiquen las opciones de uso frecuente.
19.10 Glosario
expresión condicional: Una expresión que tiene uno de dos valores, dependiendo de una condición
ción
comprensión de lista: Una expresión con un bucle entre corchetes que produce un nuevo
lista.
expresión generador: Una expresión con un objeto bucle entre paréntesis que produce un género
tor.
multiconjunto: Una entidad matemática que representa un mapeo entre los elementos de un conjunto
y el número de veces que aparecen.
fábrica: una función, generalmente pasada como parámetro, utilizada para crear objetos.
Machine Translated by Google
192 Capítulo 19. Las golosinas
19.11 Ejercicios
Ejercicio 19.1. La siguiente es una función que calcula recursivamente el coeficiente binomial.
Vuelva a escribir el cuerpo de la función usando expresiones condicionales anidadas.
Una nota: esta función no es muy eficiente porque termina calculando los mismos valores una y otra vez. Puede hacerlo
más eficiente memorizando (consulte la Sección 11.6). Pero encontrará que es más difícil de memorizar si lo escribe
usando expresiones condicionales.
Machine Translated by Google
Apéndice A
depuración
Cuando esté depurando, debe distinguir entre diferentes tipos de errores para localizarlos más
rápidamente:
• El intérprete descubre los errores de sintaxis cuando está traduciendo el código fuente a
código de bytes. Indican que hay algo mal con la estructura del programa. Ejemplo: Omitir
los dos puntos al final de una instrucción genera un mensaje algo redundante
.
• El intérprete produce errores de tiempo de ejecución si algo sale mal mientras se ejecuta el
programa. La mayoría de los mensajes de error de tiempo de ejecución incluyen información
sobre dónde ocurrió el error y qué funciones se estaban ejecutando. Ejemplo: una recursión
infinita eventualmente provoca el error de tiempo de ejecución "profundidad máxima de recursión excedida".
• Los errores semánticos son problemas con un programa que se ejecuta sin generar
mensajes de error pero no hace lo correcto. Ejemplo: es posible que una expresión no se
evalúe en el orden esperado y produzca un resultado incorrecto.
El primer paso en la depuración es averiguar con qué tipo de error está lidiando. Aunque las
siguientes secciones están organizadas por tipo de error, algunas técnicas son aplicables en más
de una situación.
A.1 Errores de sintaxis
Los errores de sintaxis generalmente son fáciles de corregir una vez que descubre cuáles son.
Desafortunadamente, los mensajes de error a menudo no son útiles. Los mensajes más
, informativo.
comunes son y ninguno de los cuales es muy
tivo
Por otro lado, el mensaje le dice en qué parte del programa ocurrió el problema.
En realidad, le dice dónde Python notó un problema, que no es necesariamente donde está el
error. A veces, el error es anterior a la ubicación del mensaje de error, a menudo en la línea
anterior.
Machine Translated by Google
194 Apéndice A. Depuración
Si está construyendo el programa de forma incremental, debe tener una buena idea de dónde está
el error. Estará en la última línea que agregó.
Si está copiando el código de un libro, comience por comparar su código con el código del libro
con mucho cuidado. Revisa cada personaje. Al mismo tiempo, recuerda que el libro puede estar
equivocado, por lo que si ves algo que parece un error de sintaxis, podría serlo.
Aquí hay algunas formas de evitar los errores de sintaxis más comunes:
1. Asegúrese de no utilizar una palabra clave de Python para un nombre de variable.
2. Verifique que tiene dos puntos al final del encabezado de cada declaración compuesta,
incluido , , ,y declaraciones.
3. Asegúrese de que todas las cadenas del código tengan comillas coincidentes. Cerciorarse
que todas las comillas son "comillas rectas", no "comillas curvas".
4. Si tiene cadenas de varias líneas con comillas triples (simples o dobles), asegúrese de haber
terminado la cadena correctamente. Una cadena sin terminar puede causar una
error al final de su programa, o puede tratar la siguiente parte del programa como
una cadena hasta que llegue a la siguiente cadena. En el segundo caso, ¡es posible que no
produzca ningún mensaje de error!
5. Un operador de apertura no cerrado—, o —hace que Python continúe con la siguiente línea
como parte de la instrucción actual. Generalmente, un error ocurre casi inmediatamente en el
Proxima linea.
6. Verifique el clásico en lugar de dentro de un condicional.
7. Verifique la sangría para asegurarse de que se alinee de la forma en que se supone que debe
hacerlo. Python puede manejar el espacio y las pestañas, pero si los mezcla puede causar
problemas. La mejor manera de evitar este problema es usar un editor de texto que conozca
Python y genere una sangría consistente.
8. Si tiene caracteres que no son ASCII en el código (incluidas cadenas y comentarios), eso
podría causar un problema, aunque Python 3 generalmente maneja caracteres que no son
ASCII. Tenga cuidado si pega texto de una página web u otra fuente.
Si nada funciona, pase a la siguiente sección...
A.1.1 Sigo haciendo cambios y no hace ninguna diferencia.
Si el intérprete dice que hay un error y usted no lo ve, puede deberse a que usted y el intérprete no
están mirando el mismo código. Verifique su entorno de programación para asegurarse de que el
programa que está editando es el que Python está tratando de ejecutar.
Si no está seguro, intente poner un error de sintaxis obvio y deliberado al comienzo del programa.
Ahora ejecútalo de nuevo. Si el intérprete no encuentra el nuevo error, no está ejecutando el nuevo
código.
Hay algunos posibles culpables:
• Editó el archivo y olvidó guardar los cambios antes de volver a ejecutarlo. Alguno
los entornos de programación hacen esto por usted, pero algunos no lo hacen.
Machine Translated by Google
A.2. Errores de tiempo de ejecución 195
• Cambió el nombre del archivo, pero todavía está ejecutando el nombre antiguo.
• Algo en su entorno de desarrollo está configurado incorrectamente. • Si está escribiendo
, el mismo nombre que uno de los módulos
un módulo y usándolo, asegúrese de no darle a su módulo
estándar de Python.
• Si está utilizando un para leer un módulo, recuerda que tienes que reiniciar el para leer un
intérprete o el uso no archivo modificado. Si vuelve a importar el módulo,
hace nada.
Si te quedas atascado y no puedes entender lo que está pasando, un enfoque es comenzar de nuevo con un
nuevo programa como “¡Hola, mundo!”, y asegurarte de que puedas ejecutar un programa conocido. Luego
agregue gradualmente las piezas del programa original al nuevo.
A.2 Errores de tiempo de ejecución
Una vez que su programa es sintácticamente correcto, Python puede leerlo y al menos comenzar a ejecutarlo.
¿Qué podría salir mal?
A.2.1 Mi programa no hace absolutamente nada.
Este problema es más común cuando su archivo consta de funciones y clases, pero en realidad no invoca
una función para iniciar la ejecución. Esto puede ser intencional si solo planea importar este módulo para
proporcionar clases y funciones.
Si no es intencional, asegúrese de que haya una llamada de función en el programa y asegúrese de que el
flujo de ejecución lo alcance (consulte "Flujo de ejecución" a continuación).
A.2.2 Mi programa se cuelga.
Si un programa se detiene y parece no hacer nada, está "colgado". A menudo, eso significa que está atrapado
en un bucle infinito o recursión infinita.
• Si hay un bucle en particular que sospecha que es el problema, agregue un declaración
inmediatamente antes del bucle que dice "entrar en el bucle" y otro inmediatamente después que dice
"salir del bucle".
Ejecuta el programa. Si recibe el primer mensaje y no el segundo, tiene un bucle infinito. Vaya a la
sección "Bucle infinito" a continuación.
• La mayoría de las veces, una recursión infinita hará que el programa se ejecute durante un tiempo y
luego produzca un error de "RuntimeError: se excedió la profundidad de recursión máxima". Si eso
sucede, vaya a la sección "Recursión infinita" a continuación.
Si no recibe este error pero sospecha que hay un problema con un método o una función recursiva,
aún puede usar las técnicas en la sección "Recursión infinita".
• Si ninguno de esos pasos funciona, comience a probar otros bucles y otras funciones recursivas
y métodos.
• Si eso no funciona, es posible que no comprenda el flujo de ejecución.
ción en su programa. Vaya a la sección "Flujo de ejecución" a continuación.
Machine Translated by Google
196 Apéndice A. Depuración
Bucle infinito
Si cree que tiene un ciclo infinito y sabe qué ciclo está causando el problema, agregue una declaración
al final del ciclo que imprima los valores de las variables en la condición y el valor de la condición.
Por ejemplo:
Ahora, cuando ejecute el programa, verá tres líneas de salida por cada tiempo a través del bucle. La
.ver los valores de y
última vez que pasó por el ciclo, la condición debería ser Si el ciclo continúa, podrá
y podría descubrir por qué no se actualizan correctamente.,
recursividad infinita
La mayoría de las veces, la recursividad infinita hace que el programa se ejecute durante un tiempo y luego produzca
a error.
Si sospecha que una función está causando una recursividad infinita, asegúrese de que haya un caso
base. Debe haber alguna condición que haga que la función regrese sin hacer una invocación recursiva.
De lo contrario, debe repensar el algoritmo e identificar un caso base.
Si hay un caso base pero el programa no parece alcanzarlo, agregue un ment al estado
comienzo de la función que imprime los parámetros. Ahora, cuando ejecute el programa, verá algunas
líneas de salida cada vez que se invoque la función y verá los valores de los parámetros. Si los
parámetros no se están moviendo hacia el caso base, obtendrá algunas ideas sobre por qué no.
Flujo de ejecución
Si no está seguro de cómo se mueve el flujo de ejecución a través de su programa, agregue
declaraciones al principio de cada función con un mensaje como “entrar en función donde está ”,
el nombre de la función.
Ahora, cuando ejecute el programa, imprimirá un rastro de cada función a medida que se invoque.
A.2.3 Cuando ejecuto el programa obtengo una excepción.
Si algo sale mal durante el tiempo de ejecución, Python imprime un mensaje que incluye el nombre de
la excepción, la línea del programa donde ocurrió el problema y un rastreo.
El rastreo identifica la función que se está ejecutando actualmente, y luego la función que la llamó, y
luego la función que la llamó, y así sucesivamente. En otras palabras, rastrea la
Machine Translated by Google
A.2. Errores de tiempo de ejecución 197
secuencia de llamadas de función que lo llevaron a donde está, incluido el número de línea en su
archivo donde ocurrió cada llamada.
El primer paso es examinar el lugar del programa donde ocurrió el error y ver si puede averiguar qué
sucedió. Estos son algunos de los errores de tiempo de ejecución más comunes:
NameError: está intentando utilizar una variable que no existe en el entorno actual.
Compruebe si el nombre está escrito correctamente, o al menos de forma coherente. Y recuerda que las
variables locales son locales; no puede hacer referencia a ellos desde fuera de la función en la que están
definidos.
TypeError: Hay varias causas posibles:
• Está tratando de usar un valor incorrectamente. Ejemplo: indexar una cadena, lista o
tupla con algo que no sea un número entero.
• Hay una discrepancia entre los elementos de una cadena de formato y los elementos
pasados para la conversión. Esto puede suceder si la cantidad de elementos no coincide o
si se solicita una conversión no válida.
• Está pasando el número incorrecto de argumentos a una función. Para los métodos, mire la
definición del método y verifique que el primer parámetro sea . Luego mira la invocación del
método; asegúrese de invocar el método en un objeto con el tipo correcto y proporcionar
los otros argumentos correctamente.
KeyError: Está intentando acceder a un elemento de un diccionario utilizando una clave que no contiene
el diccionario. Si las claves son cadenas, recuerde que las mayúsculas son importantes.
AttributeError: Está intentando acceder a un atributo o método que no existe. Marque para enumerar los
¡la ortografía! Puede usar la función incorporada Si atributos que existen.
un AttributeError indica que un objeto tiene Entonces, , eso quiere decir que es .
el problema no es el nombre del atributo, sino el objeto.
La razón por la que el objeto es none podría ser que se olvidó de devolver un valor de una
función; si llega al final de una función sin presionar una declaración, devuelve . Otra causa
común es usar el resultado de un método de lista, como ese ,
devoluciones .
IndexError: el índice que está utilizando para acceder a una lista, cadena o tupla es mayor que su
longitud menos uno. Inmediatamente antes del sitio del error, agregue una declaración a
pantalla con el valor del índice y la longitud de la matriz. ¿La matriz es del tamaño correcto?
¿Es el índice el valor correcto?
El depurador de Python () es útil para rastrear excepciones porque le permite examinar el estado del
programa inmediatamente antes del error. Puedes leer sobre en
.
A.2.4 Agregué tantos declaraciones me inundan con la salida.
Uno de los problemas con el uso de declaraciones para la depuración es que puede terminar enterrado
en la salida. Hay dos formas de proceder: simplificar la salida o simplificar el programa.
Machine Translated by Google
198 Apéndice A. Depuración
Para simplificar la salida, puede eliminar o comentar, combinarlos o declaraciones que no son de ayuda
formatear la salida para que sea más fácil de entender.
Para simplificar el programa, hay varias cosas que puede hacer. Primero, reduzca el problema en el que está
trabajando el programa. Por ejemplo, si está buscando en una lista, busque en una lista pequeña. Si el programa
toma información del usuario, déle la entrada más simple que cause el problema.
En segundo lugar, limpie el programa. Elimine el código muerto y reorganice el programa para que sea lo más fácil
de leer posible. Por ejemplo, si sospecha que el problema está en una parte profundamente anidada del programa,
intente volver a escribir esa parte con una estructura más simple. Si sospecha de una función grande, intente dividirla
en funciones más pequeñas y pruébelas por separado.
A menudo, el proceso de encontrar el caso de prueba mínimo lo lleva al error. Si encuentra que un programa
funciona en una situación pero no en otra, eso le da una pista sobre lo que está pasando.
De manera similar, reescribir un fragmento de código puede ayudarlo a encontrar errores sutiles. Si realiza un
cambio que cree que no debería afectar el programa, y lo hace, eso puede alertarlo.
A.3 Errores semánticos
De alguna manera, los errores semánticos son los más difíciles de depurar, porque el intérprete no proporciona
información sobre lo que está mal. Solo usted sabe lo que se supone que debe hacer el programa.
El primer paso es hacer una conexión entre el texto del programa y el comportamiento que está viendo. Necesita
una hipótesis sobre lo que realmente está haciendo el programa. Una de las cosas que lo hace difícil es que las
computadoras funcionan muy rápido.
A menudo deseará poder ralentizar el programa a la velocidad humana, y con algunos depuradores puede hacerlo.
Pero el tiempo que lleva insertar algunos bien colocados suele ser corto en comparación con declaraciones
configurar el depurador, insertar y eliminar puntos de interrupción y "llevar" el programa al lugar donde se produce el
error.
A.3.1 Mi programa no funciona.
Debes hacerte estas preguntas:
• ¿Hay algo que se suponía que debía hacer el programa pero que no parece estar sucediendo? Encuentre la
sección del código que realiza esa función y asegúrese de que se esté ejecutando cuando crea que debería
hacerlo.
• ¿Está sucediendo algo que no debería? Encuentre código en su programa que realice esa función y vea si se
está ejecutando cuando no debería.
• ¿Hay una sección de código que produce un efecto que no es el esperado? Asegúrese de comprender el
código en cuestión, especialmente si involucra funciones o métodos en otros módulos de Python. Lea la
documentación de las funciones a las que llama. Pruébelos escribiendo casos de prueba sencillos y
comprobando los resultados.
Machine Translated by Google
A.3. Errores semánticos 199
Para programar, necesitas un modelo mental de cómo funcionan los programas. Si escribe un
programa que no hace lo que espera, a menudo el problema no está en el programa; está en tu
modelo mental.
La mejor manera de corregir su modelo mental es dividir el programa en sus componentes
(generalmente las funciones y métodos) y probar cada componente de forma independiente. Una
vez que encuentre la discrepancia entre su modelo y la realidad, puede resolver el problema.
Por supuesto, debe construir y probar componentes a medida que desarrolla el programa.
Si encuentra un problema, debe haber solo una pequeña cantidad de código nuevo que no se sabe
si es correcto.
A.3.2 Tengo una gran expresión peluda y no es lo que espero.
Escribir expresiones complejas está bien siempre que sean legibles, pero pueden ser difíciles de
depurar. Suele ser una buena idea dividir una expresión compleja en una serie de asignaciones a
variables temporales.
Por ejemplo:
Esto se puede reescribir como:
La versión explícita es más fácil de leer porque los nombres de las variables brindan documentación
adicional y es más fácil de depurar porque puede verificar los tipos de las variables intermedias y
mostrar sus valores.
Otro problema que puede ocurrir con las expresiones grandes es que el orden de evaluación puede
no ser el esperado. Por ejemplo, si está traduciendo la expresión a Python, podría escribir:
x2π _
Eso no es correcto porque la multiplicación y la división tienen la misma precedencia y se evalúan de
izquierda a derecha. Entonces esta expresión calcula xπ/2.
Una buena manera de depurar expresiones es agregar paréntesis para hacer explícito el orden de
evaluación:
Siempre que no esté seguro del orden de evaluación, utilice paréntesis. El programa no solo será
correcto (en el sentido de hacer lo que pretendía), sino que también será más legible para otras
personas que no hayan memorizado el orden de las operaciones.
A.3.3 Tengo una función que no devuelve lo que esperaba.
Si tiene una declaración con una expresión compleja, no tiene la oportunidad de imprimir el resultado
antes de regresar. Nuevamente, puede usar una variable temporal. Por ejemplo, en lugar de:
Machine Translated by Google
200 Apéndice A. Depuración
podrías escribir:
Ahora tienes la oportunidad de mostrar el valor de antes de volver.
A.3.4 Estoy muy, muy atascado y necesito ayuda.
Primero, trate de alejarse de la computadora por unos minutos. Las computadoras emiten ondas que afectan
el cerebro y causan estos síntomas:
• Frustración y rabia.
• Creencias supersticiosas (“la computadora me odia”) y pensamiento mágico (“el programa
solo funciona cuando me pongo el sombrero al revés”).
• Programación de caminata aleatoria (el intento de programar escribiendo todos los programas posibles
y eligiendo el que hace lo correcto).
Si te encuentras sufriendo alguno de estos síntomas, levántate y sal a caminar. Cuando estés tranquilo, piensa
en el programa. ¿Qué está haciendo? ¿Cuáles son algunas posibles causas de ese comportamiento? ¿Cuándo
fue la última vez que tuvo un programa de trabajo y qué hizo a continuación?
A veces solo lleva tiempo encontrar un error. A menudo encuentro errores cuando estoy lejos de la computadora
y dejo que mi mente divague. Algunos de los mejores lugares para encontrar insectos son los trenes, las
duchas y la cama, justo antes de dormirse.
A.3.5 No, realmente necesito ayuda.
Sucede. Incluso los mejores programadores se atascan de vez en cuando. A veces trabajas en un programa
durante tanto tiempo que no puedes ver el error. Necesitas un nuevo par de ojos.
Antes de traer a alguien más, asegúrese de estar preparado. Su programa debe ser lo más simple posible y
debe estar trabajando en la entrada más pequeña que cause el error.
Debe tener declaraciones en los lugares apropiados (y el resultado que producen debe ser comprensible).
Debe comprender el problema lo suficientemente bien como para describirlo de manera concisa.
Cuando traiga a alguien para que le ayude, asegúrese de darle la información que necesita:
• Si hay un mensaje de error, ¿cuál es y qué parte del programa indica?
• ¿Qué fue lo último que hizo antes de que ocurriera este error? ¿Cuáles fueron las últimas líneas de
código que escribiste o cuál es el nuevo caso de prueba que falla?
• ¿Qué ha intentado hasta ahora y qué ha aprendido?
Cuando encuentre el error, tómese un segundo para pensar qué podría haber hecho para encontrarlo más
rápido. La próxima vez que vea algo similar, podrá encontrar el error más rápidamente.
Recuerde, el objetivo no es solo hacer que el programa funcione. El objetivo es aprender cómo hacer que el
programa funcione.
Machine Translated by Google
apéndice B
Análisis de Algoritmos
Este apéndice es un extracto editado de Think Complexity, de Allen B. Downey, también publicado
por O'Reilly Media (2012). Cuando haya terminado con este libro, es posible que desee pasar a
ese.
El análisis de algoritmos es una rama de la informática que estudia el rendimiento de los algoritmos, especialmente
su tiempo de ejecución y los requisitos de espacio. Ver
.
El objetivo práctico del análisis de algoritmos es predecir el rendimiento de diferentes algoritmos para guiar las
decisiones de diseño.
Durante la campaña presidencial de Estados Unidos de 2008, se le pidió al candidato Barack Obama que
realizara un análisis improvisado cuando visitó Google. El presidente ejecutivo, Eric Schmidt, le preguntó en
broma "la forma más eficiente de clasificar un millón de números enteros de 32 bits". Aparentemente, Obama
había recibido un aviso, porque respondió rápidamente: "Creo que el tipo de burbuja sería el camino equivocado".
Ver .
Esto es cierto: la ordenación de burbuja es conceptualmente simple pero lenta para grandes conjuntos de datos. La
respuesta que Schmidt probablemente estaba buscando es "clasificación radix" (
) 1.
El objetivo del análisis de algoritmos es hacer comparaciones significativas entre algoritmos, pero existen algunos
problemas:
• El rendimiento relativo de los algoritmos puede depender de las características del hardware, por lo que un
algoritmo puede ser más rápido en la Máquina A y otro en la Máquina B.
La solución general a este problema es especificar un modelo de máquina y analizar el número de pasos
u operaciones que requiere un algoritmo bajo un modelo dado.
• El rendimiento relativo puede depender de los detalles del conjunto de datos. Por ejemplo, algunos
algoritmos de clasificación se ejecutan más rápido si los datos ya están parcialmente ordenados; otros algoritmos
1
Pero si recibe una pregunta como esta en una entrevista, creo que una mejor respuesta es: “La forma más rápida de clasificar un
millón de números enteros es usar cualquier función de clasificación que proporcione el lenguaje que estoy usando. Su rendimiento es
lo suficientemente bueno para la gran mayoría de las aplicaciones, pero si resulta que mi aplicación es demasiado lenta, usaría un
generador de perfiles para ver dónde se gasta el tiempo. Si pareciera que un algoritmo de clasificación más rápido tendría un efecto
significativo en el rendimiento, entonces buscaría una buena implementación de la clasificación radix”.
Machine Translated by Google
202 Apéndice B. Análisis de algoritmos
correr más lento en este caso. Una forma común de evitar este problema es analizar el peor de los casos .
A veces es útil analizar el rendimiento promedio de los casos, pero eso suele ser más difícil y puede que
no sea obvio sobre qué conjunto de casos promediar.
• El rendimiento relativo también depende del tamaño del problema. Un algoritmo de clasificación que es
rápido para listas pequeñas puede ser lento para listas largas. La solución habitual a este problema es
expresar el tiempo de ejecución (o el número de operaciones) en función del tamaño del problema y
agrupar las funciones en categorías según la rapidez con la que crecen a medida que aumenta el tamaño
del problema.
Lo bueno de este tipo de comparación es que se presta a una clasificación simple de algoritmos. Por ejemplo, si
sé que el tiempo de ejecución del algoritmo A tiende a ser proporcional al tamaño de la entrada, n, y el algoritmo
2
,
B tiende a ser proporcional a n, entonces espero que A sea más rápido que B, al menos para grandes valores
de n
Este tipo de análisis viene con algunas advertencias, pero hablaremos de eso más adelante.
B.1 Orden de crecimiento
Suponga que analizó dos algoritmos y expresó sus tiempos de ejecución en términos del tamaño de la entrada: el
algoritmo A toma 100n + 1 pasos para resolver un problema con tamaño n; Algo 2 ritmo B toma n
+ n + 1 pasos.
La siguiente tabla muestra el tiempo de ejecución de estos algoritmos para diferentes tamaños de problemas:
Entrada Tiempo de ejecución de Tiempo de
ejecución de tamaño Algoritmo A Algoritmo B
10 1 001 111
100 10 001 10 101
1 000 100 001 1 001 001
10 000 1 000 001 100 010 001
En n = 10, el Algoritmo A se ve bastante mal; tarda casi 10 veces más que el algoritmo B. Pero para n = 100 son
casi iguales, y para valores más grandes, A es mucho mejor.
La razón fundamental es que para valores grandes de n, cualquier función que contenga una n crecerá 2 término
más rápido que una función cuyo término principal sea n. El término principal es el término con el exponente más
alto.
Para el algoritmo A, el término principal tiene un coeficiente grande, 100, por lo que B funciona mejor que A para
n pequeño. Pero independientemente de los coeficientes, siempre habrá algún valor de n donde an2 > bn, para
cualquier valor de a y b.
El mismo argumento se aplica a los términos no principales. Incluso si el tiempo de ejecución del Algoritmo A fuera
n + 1000000, aún sería mejor que el Algoritmo B para un n suficientemente grande.
En general, esperamos que un algoritmo con un término inicial más pequeño sea un mejor algoritmo para
problemas grandes, pero para problemas más pequeños, puede haber un punto de cruce en el que otro algoritmo
sea mejor. La ubicación del punto de cruce depende de los detalles de los algoritmos, las entradas y el hardware,
por lo que generalmente se ignora para fines de análisis algorítmico. Pero eso no significa que puedas olvidarte
de él.
Machine Translated by Google
B.1. orden de crecimiento 203
Si dos algoritmos tienen el mismo término de orden principal, es difícil decir cuál es mejor; de nuevo, la respuesta
depende de los detalles. Entonces, para el análisis algorítmico, las funciones con el mismo término principal se
consideran equivalentes, incluso si tienen diferentes coeficientes.
Un orden de crecimiento es un conjunto de funciones cuyo comportamiento de crecimiento se considera equivalente.
Por ejemplo, 2n, 100n y n + 1 pertenecen al mismo orden de crecimiento, que se escribe O(n) en notación BigOh y, a
menudo, se denomina lineal porque todas las funciones del conjunto crecen linealmente con n.
2 2
Todas las funciones con el término principal n La pertenecen a O(n ); se llaman cuadráticos.
siguiente tabla muestra algunos de los órdenes de crecimiento que aparecen más comúnmente en el análisis algorítmico,
en orden creciente de maldad.
orden de Nombre
crecimiento
O(1) constante
O(logb n) logarítmico (para cualquier b)
En) lineal
lineal rítmico
O(n logb
2
En cúbico
3 cuadrático
Enn) ) )
O(c n ) exponencial (para cualquier c)
Para los términos logarítmicos, la base del logaritmo no importa; cambiar bases es el equivalente a multiplicar por una
constante, lo que no cambia el orden de crecimiento. De manera similar, todas las funciones exponenciales pertenecen
al mismo orden de crecimiento sin importar la base del exponente. Las funciones exponenciales crecen muy
rápidamente, por lo que los algoritmos exponenciales solo son útiles para problemas pequeños.
Ejercicio B.1. Lea la página de Wikipedia sobre la notación BigOh y responda
las siguientes preguntas: 1. ¿Cuál es el
3 + n 2? ¿Qué hay de n3 +
orden de crecimiento de n3 + n 2? ¿Qué pasa con 1000000n 1000000n 2?
2
2. ¿Cuál es el orden de crecimiento de (n
+ n) ∙ (n + 1)? Antes de empezar a multiplicar, recuerda
que solo necesitas el término principal.
3. Si f está en O(g), para alguna función g no especificada, ¿qué podemos decir acerca de af + b, donde a y
b son constantes?
4. Si f1 y f2 están en O(g), ¿qué podemos decir acerca de f1 + f2?
5. Si f1 está en O(g) y f2 está en O(h), ¿qué podemos decir acerca de f1 + f2?
6. Si f1 está en O(g) y f2 es O(h), ¿qué podemos decir acerca de f1 ∙ f2?
Los programadores que se preocupan por el rendimiento a menudo encuentran este tipo de análisis difícil de tragar.
Tienen un punto: a veces los coeficientes y los términos no principales marcan una diferencia real. A veces, los detalles
del hardware, el lenguaje de programación y las características de la entrada marcan una gran diferencia. Y para
problemas pequeños, el orden de crecimiento es irrelevante.
Pero si tiene en cuenta estas advertencias, el análisis algorítmico es una herramienta útil. Al menos para problemas
grandes, el algoritmo "mejor" suele ser mejor y, a veces, es mucho mejor.
La diferencia entre dos algoritmos con el mismo orden de crecimiento suele ser un factor constante, pero la diferencia
entre un buen algoritmo y un mal algoritmo no tiene límites.
Machine Translated by Google
204 Apéndice B. Análisis de algoritmos
B.2 Análisis de las operaciones básicas de Python
En Python, la mayoría de las operaciones aritméticas son de tiempo constante; la multiplicación suele
tardar más que la suma y la resta, y la división tarda incluso más, pero estos tiempos de ejecución no
dependen de la magnitud de los operandos. Los enteros muy grandes son una excepción; en ese caso,
el tiempo de ejecución aumenta con el número de dígitos.
Las operaciones de indexación (leer o escribir elementos en una secuencia o diccionario) también son de
tiempo constante, independientemente del tamaño de la estructura de datos.
Un bucle que atraviesa una secuencia o un diccionario suele ser lineal, siempre que todas las operaciones
en el cuerpo del bucle sean de tiempo constante. Por ejemplo, sumar los elementos de una lista es lineal:
La función incorporada también es lineal porque hace lo mismo, pero tiende a ser más rápida porque es
una implementación más eficiente; en el lenguaje del análisis algorítmico, tiene un coeficiente principal
más pequeño.
un+1
Como regla general, si el cuerpo de un bucle está en O(n a ) , entonces todo el bucle está en O(n , ). El
excepto si puede mostrar que el bucle sale después de un número constante de iteraciones. Si un bucle ejecuta
k veces independientemente de n, entonces el bucle está en O(n a ), incluso para k grande.
Multiplicar por k no cambia el orden de crecimiento, pero tampoco lo hace dividir. Entonces si el
el cuerpo de un bucle está en O(n a ) y se ejecuta n/k veces, el bucle está en O(nun+1 ), incluso para k grande.
, y que son de tiempo
La mayoría de las operaciones de cadenas y tuplas son lineales, excepto la indexación
constante. Las funciones integradas y son lineales. El tiempo de ejecución de una operación de división es
proporcional a la longitud de la salida, pero independiente del tamaño de la entrada.
La concatenación de cadenas es lineal; el tiempo de ejecución depende de la suma de las longitudes de los
operandos.
Todos los métodos de cadena son lineales, pero si las longitudes de las cadenas están limitadas por una
constante, por ejemplo, operaciones en caracteres individuales, se consideran tiempo constante. El
método de la cadena es lineal; el tiempo de ejecución depende de la longitud total de las cadenas.
La mayoría de los métodos de lista son lineales, pero hay algunas excepciones:
• Agregar un elemento al final de una lista es un tiempo constante en promedio; cuando se queda
sin espacio, ocasionalmente se copia en una ubicación más grande, pero el tiempo total para n
operaciones es O(n), por lo que el tiempo promedio para cada operación es O(1).
• Eliminar un elemento del final de una lista es un tiempo constante.
• La clasificación es O(n log n).
La mayoría de las operaciones y métodos del diccionario son de tiempo constante, pero hay algunas excepciones:
• El tiempo de ejecución de es proporcional al tamaño del diccionario pasado como pa
rameter, no el diccionario que se actualiza.
• , y son de tiempo constante porque devuelven iteradores. Pero si
recorre los iteradores, el ciclo será lineal.
Machine Translated by Google
B.3. Análisis de algoritmos de búsqueda 205
El rendimiento de los diccionarios es uno de los pequeños milagros de la informática. Veremos cómo funcionan en la
Sección B.4.
Ejercicio B.2. Lea la página de Wikipedia sobre algoritmos de clasificación en
y responda las siguientes preguntas:
1. ¿Qué es una "clasificación de comparación"? ¿Cuál es el mejor orden de crecimiento en el peor de los casos para una comparación
¿clasificar? ¿Cuál es el mejor orden de crecimiento en el peor de los casos para cualquier tipo de algoritmo?
2. ¿Cuál es el orden de crecimiento del tipo de burbuja y por qué Barack Obama piensa que es “el mal
¿camino a seguir?"
3. ¿Cuál es el orden de crecimiento de tipo radix? ¿Qué condiciones previas necesitamos para usarlo?
4. ¿Qué es un tipo estable y por qué podría ser importante en la práctica?
5. ¿Cuál es el peor algoritmo de clasificación (que tiene nombre)?
6. ¿Qué algoritmo de clasificación usa la biblioteca C? ¿Qué algoritmo de clasificación usa Python? Son estos
algoritmos estables? Es posible que tengas que buscar en Google para encontrar estas respuestas.
7. Muchos de los tipos que no son de comparación son lineales, entonces, ¿por qué Python usa una comparación O (n log n) ?
tipo ison?
B.3 Análisis de algoritmos de búsqueda
Una búsqueda es un algoritmo que toma una colección y un elemento objetivo y determina si el objetivo está en la
colección, a menudo devolviendo el índice del objetivo.
El algoritmo de búsqueda más simple es una “búsqueda lineal”, que recorre los elementos de la colección en orden,
deteniéndose si encuentra el objetivo. En el peor de los casos, tiene que recorrer toda la colección, por lo que el tiempo
de ejecución es lineal.
El operador de secuencias utiliza una búsqueda lineal; también lo hacen los métodos de cadena como y
.
Si los elementos de la secuencia están en orden, puede usar una búsqueda de bisección, que es O (log n). La búsqueda
de bisección es similar al algoritmo que puede usar para buscar una palabra en un diccionario (un diccionario en papel,
no la estructura de datos). En lugar de comenzar desde el principio y verificar cada elemento en orden, comience con
el elemento en el medio y verifique si la palabra que está buscando viene antes o después. Si viene antes, busca en la
primera mitad de la secuencia. De lo contrario, busca la segunda mitad. De cualquier manera, reduce la cantidad de
elementos restantes a la mitad.
Si la secuencia tiene 1.000.000 de elementos, se necesitarán unos 20 pasos para encontrar la palabra o concluir que
no está allí. Eso es unas 50.000 veces más rápido que una búsqueda lineal.
La búsqueda en bisección puede ser mucho más rápida que la búsqueda lineal, pero requiere que la secuencia esté en
orden, lo que puede requerir trabajo adicional.
Existe otra estructura de datos, llamada tabla hash , que es aún más rápida (puede realizar una búsqueda en tiempo
constante) y no requiere que se ordenen los elementos. Los diccionarios de Python se implementan mediante tablas
hash, por lo que la mayoría de las operaciones del diccionario, incluido el operador, son de tiempo constante.
Machine Translated by Google
206 Apéndice B. Análisis de algoritmos
B.4 Tablas hash
Para explicar cómo funcionan las tablas hash y por qué su rendimiento es tan bueno, empiezo con una
implementación simple de un mapa y lo mejoro gradualmente hasta que se convierte en una tabla hash.
Uso Python para demostrar estas implementaciones, pero en la vida real no escribirías código como este en
Python; ¡simplemente usarías un diccionario! Entonces, para el resto de este capítulo, debe imaginar que los
diccionarios no existen y desea implementar una estructura de datos que se asigne de claves a valores. Las
operaciones que tienes que implementar son:
: agregue un nuevo elemento que se asigne de clave a valor . Con un diccionario de Python, esta
operación se escribe .
: Busque y devuelva el valor que corresponde a key . Con un diccionario de Python, esta operación
, se escribe o .
Por ahora, supongo que cada tecla solo aparece una vez. La implementación más simple de esta interfaz
utiliza una lista de tuplas, donde cada tupla es un par clavevalor.
agrega una tupla clavevalor a la lista de elementos, lo que requiere un tiempo constante.
usa un bucle para buscar en la lista: si encuentra la clave de destino, devuelve la correspondiente
valor; de lo contrario, plantea un . Entonces es lineal.
Una alternativa es mantener la lista ordenada por clave. Entonces podría usar una búsqueda de bisección,
que es O (log n). Pero insertar un nuevo elemento en medio de una lista es lineal, por lo que esta podría no
ser la mejor opción. Hay otras estructuras de datos que se pueden implementar y en tiempo de registro, pero
aún no son tan buenas como el tiempo constante, así que sigamos adelante.
Una forma de mejorar es dividir la lista de pares clavevalor en listas más pequeñas.
Aquí hay una implementación llamada que es una lista ,de 100 LinearMaps. Como veremos en un segundo, el
orden de crecimiento sigue siendo lineal, pero es un paso en el camino hacia las tablas hash:
Machine Translated by Google
B.4. tablas hash 207
hace una lista de s.
es utilizado por y para averiguar en qué mapa colocar el nuevo elemento o en qué mapa
buscar.
utiliza la función integrada que toma
, casi cualquier objeto de Python y devuelve un número
entero. Una limitación de esta implementación es que solo funciona con claves hashable.
Los tipos mutables como listas y diccionarios no se pueden modificar.
Los objetos hashable que se consideran equivalentes devuelven el mismo valor hash, pero lo contrario
no es necesariamente cierto: dos objetos con valores diferentes pueden devolver el mismo valor hash.
usa el operador de módulo para envolver los valores hash en el rango de 0 a, por lo que el
, resultado es un índice legal en la lista. Por supuesto, esto significa que muchos
valores hash diferentes se ajustarán al mismo índice. Pero si la función hash distribuye las cosas de
manera bastante uniforme (que es para lo que están diseñadas las funciones hash), entonces esperamos
n/100 elementos por LinearMap.
Dado que el tiempo de ejecución de es proporcional a la cantidad de elementos, esperamos que BetterMap sea
unas 100 veces más rápido que LinearMap. El orden de crecimiento sigue siendo lineal, pero el coeficiente
principal es más pequeño. Eso es bueno, pero aún no es tan bueno como una tabla hash.
Aquí (finalmente) está la idea crucial que hace que las tablas hash sean rápidas: si puede mantener limitada
la longitud máxima de LinearMaps, es un tiempo constante. Todo lo que tiene que hacer es realizar un
seguimiento de la cantidad de elementos y, cuando la cantidad de elementos por LinearMap supere un umbral,
cambie el tamaño de la tabla hash agregando más LinearMaps.
Aquí hay una implementación de una tabla hash:
Machine Translated by Google
208 Apéndice B. Análisis de algoritmos
Hacer una nueva , dos veces más grande que el anterior, y luego "rehace" el
elementos del mapa antiguo al nuevo.
El refrito es necesario porque al cambiar el número de LinearMaps cambia el denominador del operador de
módulo en . Eso significa que algunos objetos que solían generar hash en el mismo LinearMap se dividirán (que
es lo que queríamos, ¿verdad?).
El refrito es lineal, por lo que es lineal, lo que puede parecer malo, ya que prometí que
sería un tiempo constante. Pero recuerde que no tenemos que cambiar el tamaño cada vez, por lo general, es
el tiempo es constante y solo ocasionalmente es lineal. La cantidad total de trabajo para los tiempos de norte
ejecución es proporcional a n, por lo que el tiempo promedio de cada uno es un tiempo constante.
Para ver cómo funciona esto, piense en comenzar con un HashTable vacío y agregar una secuencia de elementos.
Comenzamos con 2 LinearMaps, por lo que las primeras 2 adiciones son rápidas (no se requiere cambiar el tamaño).
Digamos que toman una unidad de trabajo cada uno. El próximo agregado requiere un cambio de tamaño, por lo que
tenemos que repetir los dos primeros elementos (llamémoslos 2 unidades de trabajo más) y luego agregar el tercer
elemento (una unidad más). Agregar el siguiente artículo cuesta 1 unidad, por lo que el total hasta ahora es de 6
unidades de trabajo para 4 artículos.
El siguiente cuesta 5 unidades, pero los siguientes tres son solo una unidad cada uno, por lo que el total es de 14 unidades para los
primeros 8 agregados.
El siguiente cuesta 9 unidades, pero luego podemos agregar 7 más antes del próximo cambio de tamaño, por lo que el total es de 30
unidades para los primeros 16 agregados.
Después de 32 agregados, el costo total es de 62 unidades y espero que esté comenzando a ver un patrón.
Después de n sumas, donde n es una potencia de dos, el costo total es 2n − 2 unidades, por lo que el trabajo
promedio por suma es un poco menos de 2 unidades. Cuando n es una potencia de dos, ese es el mejor de los
casos; para otros valores de n el trabajo promedio es un poco más alto, pero eso no es importante. Lo importante
es que es O(1).
La Figura B.1 muestra cómo funciona esto gráficamente. Cada bloque representa una unidad de trabajo. Las
columnas muestran el trabajo total para cada adición en orden de izquierda a derecha: las dos primeras cuestan 1
unidad cada una, la tercera cuesta 3 unidades, etc.
Machine Translated by Google
B.5. Glosario 209
Figura B.1: El costo de agregar una tabla hash.
El trabajo adicional de refrito aparece como una secuencia de torres cada vez más altas con un espacio cada vez mayor
entre ellas. Ahora, si derriba las torres y distribuye el costo de cambiar el tamaño entre todas las adiciones, puede ver
gráficamente que el costo total después de n adiciones es 2n − 2.
Una característica importante de este algoritmo es que cuando cambiamos el tamaño de HashTable, crece geométricamente;
es decir, multiplicamos el tamaño por una constante. Si aumenta el tamaño aritméticamente, agregando un número fijo
cada vez, el tiempo promedio por es lineal.
Puede descargar mi implementación de HashMap desde pero recuerde que no
, hay razón para usarla; si quieres un mapa, solo usa un
Diccionario de Python.
B.5 Glosario
análisis de algoritmos: Una forma de comparar algoritmos en términos de su tiempo de ejecución y/o
requerimientos de espacio.
modelo de máquina: una representación simplificada de una computadora utilizada para describir algoritmos.
Peor caso: La entrada que hace que un algoritmo determinado se ejecute más lentamente (o requiera la mayor cantidad de
espacio).
término principal: En un polinomio, el término con el exponente más alto.
punto de cruce: El tamaño del problema donde dos algoritmos requieren el mismo tiempo de ejecución o
espacio.
orden de crecimiento: Un conjunto de funciones que crecen todas de una manera considerada equivalente para propósitos
de análisis de algoritmos. Por ejemplo, todas las funciones que crecen linealmente pertenecen al mismo orden de
crecimiento.
Notación BigOh: Notación para representar un orden de crecimiento; por ejemplo, O(n) repre
envía el conjunto de funciones que crecen linealmente.
lineal: Un algoritmo cuyo tiempo de ejecución es proporcional al tamaño del problema, al menos para grandes
tamaños de problemas.
2 , donde n es una medida de
cuadrático: un algoritmo cuyo tiempo de ejecución es proporcional al tamaño del
problema n.
búsqueda: El problema de localizar un elemento de una colección (como una lista o diccionario) o determinar que no está
presente.
Machine Translated by Google
210 Apéndice B. Análisis de algoritmos
hashtable: una estructura de datos que representa una colección de pares clavevalor y realiza
búsquedas en tiempo constante.
Machine Translated by Google
Índice
abecedario, 73, 84 aumentado, 93, 100
función abs, 52 elemento, 74, 90,
ruta absoluta, 139, 145 116 tupla, 116, 117, 119, 122
acceso, 90 declaración de asignación, 9
acumulador, 100 atributo, 153, 169
histograma, 127 __dict__, 168
lista, 93 clase, 172, 180
cadena, 175 inicializando, 168
suma, 93 instancia, 148, 153, 172, 180
función de Ackermann, 61, 113 AttributeError, 152, 197
método de suma, asignación aumentada, 93, 100
165 suma con acarreo, 68 Austen, Jane, 127
algoritmo, 67, 69, 130, 201 caso promedio, 202
MD5, 146 costo promedio, 208
raíz cuadrada, 69
aliasing, 95, 96, 100, 149, 151, 170 maldad, caso
copiar para evitar, 99 base 203, 44, 47
todo, evaluación comparativa, 133,
186 alfabeto, , 134
37 ejecución alternativa, 41 206 grande, expresión peluda, 199
ambigüedad, 5 Notación BigOh, 209
anagrama, 101 notación bigoh, 203
conjunto de anagramas, búsqueda binaria, 101
123, 145 análisis de algoritmos , 201, bingo, 123
209 análisis de primitivas, 204 y cumpleaños,
operador, 40 160 paradoja de
cualquiera, cumpleaños, 101
185 método de adición, 92, 97, 101, 174, 175 módulo de bisección, 101
función de arco, 31 búsqueda de bisección, 101, 205
Espiral de Arquímedes, 38 bisección, depuración
argumento, 17, 19, 21, 22, 26, 97 por, 68 operador
recopilar, 118 bit a bit, 3
palabra clave, 33, 36, cuerpo, 19, 26, 65 tipo booleano,
191 40 expresión booleana,
lista, 97 opcional, 76, 79, 95, 107, 40, 47 función booleana,
184 posicional, 164, 169, 190 54 operador booleano, 76 préstamo, resta
variable tupla de longitud, 118 con, 68, 159
dispersión de argumentos, acotado,
118 operador aritmético, 3 207 corchete
declaración de afirmación, 159, ondulado, 103 operador de
160 asignación, 14, 63, 89 corchete, 71, 90, 116 rama, 41, 47
Machine Translated by Google
212 Índice
declaración de interrupción, declaración compuesta, 41, 47
66 clasificación de concatenación, 12, 14, 22, 73, 74, 95 lista, 91,
burbuja, 201 97, 101 condición,
error, 6, 7, 13 41, 47, 65, 196 condicional, 194
peor, 170 función encadenado, 41, 47
incorporada anidado, 42, 47
cualquiera, 185, 186 objeto de bytes, 141, 145 ejecución
condicional , 41 expresión
calculadora, 8, 15 condicional, 183, 191 declaración condicional,
gráfico de llamadas, 109, 112 41, 47, 55, 184 verificación de consistencia, 111,
Charla de coche, 88, 113, 124 158 tiempo constante, 208
clase de tarjeta, 172 contribuyentes, tipo de
tarjeta, jugar, 171 llevar, conversión vii, 17 copia
suma con, 68, 156, 158 atrapar, 145 condicional profunda, 152
encadenado, superficial,
41, 47 carácter, 71 suma de 152
verificación, 143, rebanada, 74,
146 clase infantil, 176, 180 92 para evitar
función de elección, 126 aliasing , 99
función de círculo, 31 módulo de copia, 151 copia
definición circular, clase de objetos, 151 método
55, 4, 147, 153 de conteo, 79
Tarjeta, 172 Contador, 187
niño, 176, 180 contador, 75, 79, 104, 111 conteo
Cubierta, 174 y bucle, 75
Mano, 176 Creative Commons, punto de
Canguro, 170 padre, cruce vi, 202, 209 crucigramas, 83
176 suma acumulada,
Punto, 148, 165 100
Rectángulo, 149
Tiempo, 155 encapsulación de datos, 179, 181
atributo de clase, 172, 180 estructura de datos, 122, 123, 132 base
definición de clase, 147 de datos, 141, 145 objeto
diagrama de clase, 177, 181 de base de datos, 141
objeto de clase, 148, 153, 190 módulo de fecha y hora, 160
método de cierre, 138, 141, 143 módulo dbm, 141 código
método __cmp__, 173 muerto, 52, 60, 198 depurador
Conjetura de Collatz, 65 (pdb), 197 depuración, 6, 7 ,
colecciones, 187, 188, 190 dos 13, 36, 46, 59, 77, 87, 98, 111, 122, 133, 144, 152, 159, 168,
puntos, 19, 194 178, 185, 193 por bisección, 68 respuesta
comentario, 12, 14 emocional,
conmutatividad, 12, 167 función 6, 200 experimental,
de comparación, 52 algoritmos 25 pato de goma, 134 superstición,
de comparación, 201 cadena de 200 cubierta, 171
comparación, 77
tupla, 116,
174 clasificación de
comparación, 205 composición, Clase de baraja, 174
19 , 22, 26, 54, 174 baraja, naipes, 174
Machine Translated by Google
Índice 213
declaración, 110, 112 walk, 140
decremento, 64, 69 trabajando, 139
copia profunda, 152, 153 despacho
función de copia profunda, 152 basado en tipos, 167
palabra clave def, 19 despacho, basado en tipos, 166
valor predeterminado, 129, 134, divisibilidad, 39
165 evitando mutable, 170 división de
dictado coma flotante, 39 piso,
39, 46, 47 divmod,
predeterminado, 117, 158 docstring,
188 35, 37, 148 notación de
definición puntos, 18, 26, 76, 148, 162, 172
circular, 55 clase, Doble Día, 160 letras
147 función, 19 dobles, 88
recursivo , 124 del operador, 94 Doyle, Arthur Conan, 25
eliminación, elemento duplicados, 101, 113, 146, 187
de lista, 94 delimitador, 95, 100
desarrollo diseñado, 160 elemento, 89, 100
determinista, 126, 134 plan eliminación de elementos,
de desarrollo, 36 encapsulación de 94 palabra clave elif, 42
datos, 179, 181 Elkner, Jeff, v, vi
diseñado, 158 encapsulación y generalización, puntos
35 incremental, 52, 193 suspensivos, 19 otra
prototipo y parche, 156, 158 palabra clave, 41
programación de recorrido aleatorio, 134, 200 dirección de correo electrónico, 117
reducción, 85, 87 objeto incrustado,
gráfico de 150, 153, 170 copia, 152 depuración
llamada de emocional, 6, 200
diagrama, 112 lista vacía, 89 cadena
clase, 177, 181 objeto, 148, 150, 152, 153, vacía, 79, 95 encapsulación, 32, 36 , 54, 69,
155, 173 pila, 75, 177 codificar,
23, 97 estado, 9 , 63, 78, 90, 96, 108, 120, 148, 150, 171, 180 cifrar,
152, 155, 173 171 carácter de fin de línea, 144
atributo __dict__, 168 función enumerar función, 119 enumerar
dict, 103 diccionario, objeto, 119 épsilon, 67
103, 112, 120, 197 inicializar, 120 igualdad y
invertir, 107 asignación, 63 equivalencia, 96, 152
buscar, 106 equivalente, 100
bucle con , 106
búsqueda inversa, 106 error
resta, 129 transversal, tiempo de ejecución, 13, 44,
120, 168 métodos 46, 193 semántica, 13, 193,
de diccionario, 204 198 forma,
módulo dbm, 141 resta de 122 sintaxis, 13,
diccionario, 186 diff, 193 comprobación de
146 errores, 58 mensaje de error, 7,
13, 193 función eval,
Dijkstra, Edsger, 87 69 evaluar, 10
función dir, 197 excepción, 13, 14, 193 , 196
directorio, 139, 145 Error de atributo, 152, 197
Machine Translated by Google
214 Índice
Error de archivo no encontrado, 140 bucle for, 30, 44, 72, 91, 119, 184
Error de índice, 72, 78, 90, 197 lenguaje formal, 4, 7
Error de clave, 104, 197 operador de formato, 138, 145, 197
Error de búsqueda, 107 secuencia de formato, 138, 145
Error de nombre, 22, 197 cadena de formato, 138, 145
Error de desbordamiento, 46 marco, 23, 26, 44, 56, 109
Error de tiempo de ejecución, 45 Licencia de documentación libre, GNU, frecuencia v,
Detener iteración, 185 vi, 105 letras, 123
Error de sintaxis, 19 palabras,
Error de tipo, 72, 74, 108, 116, 118, 139, 164, 125, 134 función
197 fructífera, 24, 26 frustración,
UnboundLocalError, 110 200 función, 3, 17,
ValueError, 46, 117 19, 25, 161 abs, 52 ack, 61, 113
excepción, captura, 140 arc, 31
ejecutar, 11, 14 elección, 126
función existente, 139 círculo,
depuración experimental, 25, 134 exponente, 31 comparar,
202 crecimiento 52 copia
exponencial, 203 expresión, 10, profunda, 152
14 grande y peludo, dict, 103 dir, 197
199 booleano, 40, 47 enumerar,
condicional, 183 , 119
191 generador, 185, 186, evaluar, 69 existe,
191 método extendido, 92 139
factorial, 56,
183 fibonacci, 57,
factorial, 183 109 encontrar, 74
función factorial, 56, 58 fábrica, flotar, 17
191 función de fructífero,
fábrica, 188, 189 24 getattr,
Falso valor especial, 40 168 getcwd,
Último teorema de Fermat, 48 139 hasattr,
función de fibonacci, 57, 109 153, 168 entrada, 45
archivo, int, 17 es
137 permiso, 140 instancia,
lectura y escritura, 137 objeto de 58, 153, 166 len, 26, 72, 104
archivo, 83, 87 lista, 94 log, 18
nombre de archivo, 139
FileNotFoundError, 140 patrón
de filtro, 93, 100, 184 función de matemáticas,
búsqueda, 74 bandera, 18 max, 117, 118
110, 112 función min, 117, 118
flotante, 17 tipo abierto, 83 , 84, 137, 140, 141
flotante, 4 punto polígono, 31
flotante, 4, 7, 67, 183 división de popen, 142
punto flotante, 39 división de piso, definido por programador, 22, 129
39, 46, 47 flujo de ejecucion, randint, 101, 126
21, 26, 58, 59, 65, 178, 196 flor, 37 carpeta, 139 aleatorio, 126
razones para, 24
recursivo, 43
Machine Translated by Google
Índice 215
recargar, 144, 195 hashable, 108, 112, 120
repr, 144 HashMap, tabla
invertido, 121 hash 207, 112, 206,
aleatorio, 175 encabezado 210, 19, 25, 194
ordenado, 99, 106, 121 Hola, mundo, 3
sqrt, 18, 53 hexadecimal, 148
str, 18 lenguaje de alto nivel, 6
suma, 118, 185 histograma, 105
trigonométrico, 18 selección aleatoria, 126, 130
tupla, 115 frecuencias de palabras, 127
tupla como valor de retorno, Holmes, Sherlock, 25
117 tipo, homófono, 113
153 hipotenusa, 54
vacío ,
24 zip, 118 argumento de idéntico, 100
función, 21 llamada de identidad, 96, 152
función, 17, 26 composición declaración if, 41
de función, 54 definición de función, inmutabilidad, 74, 79, 97, 108, 115, 121
19, 20, 25 marco de función, 23, 26, 44, implementación, 105, 112, 132, 169
56, 109 objeto de declaración de importación,
función, 27 parámetro de 26, 144
función, 21 sintaxis de operador, 205 operador in, 76,
función, 162 tipo de 85, 90, 104 incremento, 64, 69,
función, 20 157, 163 desarrollo incremental, 60, 193
modificador, sangría, 19, 162, 194 índice,
157 puro, 156 estilo de programación funcional, 158, 16071, 78, 79, 90, 103, 197 bucle con,
86, 91 negativo, 72
función gamma, 58 rebanada, 73 ,
reunir, 118, 123, 190 91 a partir de
GCD (máximo común divisor), 61 cero, 71, 90
generalización, 32, 36, 85, 159 IndexError, 72, 78, 90, 197
expresión generadora, 185, 186, 191 indexación, 204
objeto generador, 185 bucle infinito, 65, 69, 195, 196
cambio de tamaño recursividad infinita, 44, 47, 58, 195, 196
geométrico, 209 herencia, 176, 178, 180, 190 método
método get, 105 función init, 164, 168, 172, 174, 176 variable de
getattr, 168 función inicialización,
getcwd, 139 declaración global, 69 inicialización
110, 112 variable global, 110, (antes de actualizar), 64 función de
112 actualizar, 110 entrada, 45 instancia,
Licencia de documentación libre GNU, v, vi 148, 153 como
máximo común divisor (MCD), 61 cuadrícula, argumento, 149
27 patrón como valor devuelto,
guardián, 59, 60, 78 150 atributo de instancia, 148, 153, 172,
180 instanciar,
Clase de mano, 153 instanciación,
176 colgando, 195 148 función int, 17
relación HASA, 177, 180 función tipo int, 4
hasattr, 153, 168 función hash, entero, 4, 7
108, 112, 207 modo interactivo, 11, 14, 24
Machine Translated by Google
216 Índice
interfaz, 33, 36, 169, 179 Principio de sustitución de Liskov, 179
palabras entrelazadas, 101 lista, 89, 94, 100, 121, 184
interpretar, como argumento,
6 intérprete, 2 97 concatenación, 91, 97,
invariante, 159, 160 101
invertir diccionario, 107 copia, 92
invocación, 76, 79 elemento,
es operador, 95, 152 90 vacío, 89
relación ISA, 177, 180 función función,
de instancia, 58, 153, 166 elemento, 94 índice, 90
74, 79, 89, 103 pertenencia,
diccionario, 112 90 método, 92
asignación de elemento, 74, 90, anidado, 89, 91
116 actualización de objetos, 174
de elemento, 91 de tuplas, 119
método de operación, 91
elementos, 120 iteración, 64, 69 iterador , 119–121, 123, 204 repetición,
91 corte, 91
, 204
transversal, 91 lista de
método de unión, 95, 175 comprensión, 184,
191 métodos
Clase canguro, 170
de lista, 204 literalidad,
tecla, 103, 112
5 variable local,
par clavevalor, 103, 112, 120
22, 26 función
entrada de teclado, 45
de registro, 18 logaritmo,
Error de clave, 104, 197
, 206 135 logarítmico
crecimiento,
palabra clave, 10, 14, 194
def, 19 203 operador lógico, 40 búsqueda, 112 búsqueda, diccionario, 106
elif, 42 LookupError, 107
else, 41 loop, 31, 36, 65, 119
condition, 196
argumento de palabra clave, 33, 36, 191
curva de Koch, 49 for, 30, 44, 72, 91
infinite, 65, 196
lenguaje nested, 174
formal, 4 transversal,
natural, 4 72 while,
seguro, 13 64 loop variable, 184
Turing completo, 55 looping
coeficiente principal, 202 con diccionarios, 106
término principal, 202, con índices, 86, 91
209 acto de fe, 57 con cadenas, 75
función len, 26, 72, 104 bucles y conteos, 75 lenguaje
frecuencia de letras, de bajo nivel, 6 ls
123 rotación de letras, (comando Unix), 142
80, 113
lineal, 209 crecimiento modelo de máquina, 201,
lineal, 203 búsqueda 209 principal, 23, 43,
, lineal, 205 206 110, 144 mantenible,
Linux, 25 169 patrón de mapa,
lipograma, 84 93, 100 mapa a, 171
Machine Translated by Google
Índice 217
cartografía, 112, 131 método, lista, 92
análisis de Markov, 130 Meyers, Chris, función
mashup, 132 vi min, 117, 118
función matemática, Moby Project, 83
18 matplotlib, 135 modelo, mental, 199
función máxima, 117, 118 modificador, 157, 160
McCloskey, Robert, 73 md5, módulo, 18, 26
143 bisecar, 101
algoritmo MD5, 146 colecciones, 187, 188, 190
md5sum, 146 copiar, 151
búsqueda fecha y hora, 160
binaria de membresía, dbm, 141
101 búsqueda de os, 139
bisección, 101 pickle, 137, 142
pprint, 112
perfil , 133
diccionario, 104 aleatorio, 101, 126, 175
lista, 90 conjunto, 113 recargar, 144, 195
memo, 109, 112 modelo mental, 199 dejar de lado,
metáfora, invocación 142 cadena,
de método, 163 metátesis, 123 125 forma de
método, 36, 75, estructura,
161 , 169 __cmp__, 122 tiempo, 101 objeto de
173 módulo, 18, 143 módulo,
__str__, 165, 174 agregar, 165 escritura, 143 operador de módulo, 39, 47
agregar, 92, 97, 101, Monty Python y el Santo Grial, 156
174, 175 MP3, 146
cerrar, 138, mro method, 179
141, 143 multiline string, 35, 194
contar, 79 extender, 92 multiplicity (in class diagram), 178, 181 multiset,
obtener, 105 187 mutability,
iniciar, 164, 74, 90, 92, 96, 111, 115, 121, 151 mutable object, as
172, 174, default value, 170
176 elementos ,
120 unirse, nombre variable incorporada, 144
95, 175 tupla nombrada, 190
mro, 179 pop, 94, Error de nombre, 22, 197
175 radd, 167 NaN, 183
leer, 143 leer lenguaje natural, 4, 7 índice
línea, 83, 143 negativo, 72 condicional
eliminar, 94 anidado, 42, 47 lista anidada,
reemplazar, 125 89, 91, 100 nueva línea, 45,
establecer 175
por defecto, 113 método de Newton, 66
ordenar, 92, 99, Ninguno valor especial, 24, 26, 52, 92, 94
176 dividir, 95, NingunoTipo tipo, 24 no
117 cadena, operador, 40
79 número, aleatorio, 126
despojar, 84, 125 traducir, 125
actualizar, 120 valores, 104 anular, 92 orden de resolución dObama, Barack,
179 s2
el método, 01 del método, 162
intaxis
Machine Translated by Google
218 Índice
objeto, 74, 79, 95, 96, 100 u operador, 40
bytes, 141, 145 orden de crecimiento, 202,
clase, 147, 148, 153, 190 209 orden de operaciones, 11, 14,
copiando, 151 199 módulo os,
Contador, 187 139 otro (nombre del parámetro), 164
base de datos, OverflowError, 46
141 predeterminado, sobrecarga, 169
188 incrustado, 150, 153, 170 anular, 129, 134, 165, 173, 176, 179
enumerar, 119
archivo, 83, palíndromo, 61, 80, 86, 88
87 función, 27 parámetro, 21, 23, 26, 97
generador, 185 recopilar,
módulo, 143 118 opcional, 129,
mutable, 151 165 otro,
tupla con nombre, 164 uno
190 mismo, 163 clase
tubería, 145 principal, 176,
impresión, 180 paréntesis
162 argumento en,
conjunto, 186 zip, 123 diagrama de objetos, 148, 17 vacío, 19, 76
150, 152, 153, 155, 173 diseño parámetros en , 21, 22
orientado a objetos, 169 lenguaje entrada de
orientado a objetos, 169 programación orientada a clase
principal, 176 tuplas,
objetos, 147, 161, 169, 176 odómetro, 88 115 análisis, 5,
Olin College, 7 declaración
función abierta v, 83, 84, 137, 140, 141 de aprobación,
operando, 14 41 ruta,
operador, 7 139, 145 absoluta,
y, 40 139 relativa, 139 filtro
aritmética, 3 de patrón, 93,
bit a bit, 3 100, 184 guardián,
booleano, 76 59, 60, 78 mapa, 93, 100
corchete, 71, 90, 116 reducir, 93,
del, 94 100 buscar, 75, 79, 85, 107, 186 intercambiar, 116 pdb (depurador de Python), 197
formato, 138, 145, 197 PEMDAS, 11
en, 76, 85, 90, 104 permiso, archivo, 140
es, 95, 152 persistencia, 137, 145
lógico, 40 pi, 18, 70
módulo, 39, 47 no, módulo de decapado, 137,
40 o, 142 decapado,
40 142 pie,
sobrecarga, 169 37 tubería,
relacional, 40, 173 142 objeto de
rebanada, 73, 79, 91, 98, tubería, 145 texto sin
116 formato, 83, 125 desarrollo
cadena, 12 planificado, 158 poesía, 5
actualización, 93 sobrecarga del Clase de puntos, 148,
operador, 166, 173 argumento opcional, 76, 165 puntos, matemática, 147
79, 95, 107, 184 parámetro opcional, 129, 165 póquer, 171, 181
Machine Translated by Google
Índice 219
función poligonal, 31 programación de recorrido aleatorio, 134, 200
polimorfismo, 168, 169 método rango, 171
pop, 94, 175 función popen, método de lectura, 143
142 portabilidad, 6 método readline, 83, 143
argumento reasignación, 63, 68, 90, 110
posicional, 164, 169, 190 postcondición, 36, Clase de rectángulo, 149
59, 179 módulo pprint, 112 recursión, 43, 47, 55, 57 caso
precedencia, 199 base, 44 infinito,
precondición, 36, 37, 44, 58, 196 definición
59, 179 prefijo, 131 letra bonita, 112 recursiva, 56, 124 árbol rojonegro,
función de 206 patrón reducido, 93,
impresión, 3 100 palabra reducible, 113,
declaración de 124 reducción a un problema
impresión, 3, 7, 165, 197 resolución de previamente resuelto, 85 reducción a un problema
problemas, 1, 6 módulo de
perfil, 133 programa, 1, 6 previamente resuelto, 87 redundancia, 5 refactorización, 34–
prueba de 36,
programa, 87 función 180 referencia, 96,
definida por el programador, 22, 129 tipo definido 97, 100 aliasing, 96 refrito, 208
por el programador, 147, 153, 155, 162, 165, 173 operador relacional, 40, 173
ruta relativa,
Proyecto Gutenberg, 125 139, 145 recargar
indicador, 2, 6, 45 función, 144, 195 eliminar método,
prosa, 5 94 repetición, 30 lista, 91
prototipo y parche, 156, 158, 160 reemplazar método, 125 repr
pseudoaleatorio, 126, 134 función función, 144 representación,
pura, 156, 160 147, 149, 171
Rompecabezas, 88, 113, 124
teorema de Pitágoras, 52 declaración de retorno, 44,
Pitón 51, 199 valor de retorno,
corriendo, 2 17, 26, 51, 150 tupla, 117 búsqueda
Pitón 2, 2, 3, 33, 40, 45 inversa, 112 búsqueda inversa,
Python en un navegador, 2 diccionario, 106 par de palabras
Python en cualquier lugar, 2 inversas, 101
función inversa, 121
cuadrático, 209 rotación
crecimiento cuadrático, 203
comillas, 3, 4, 35, 74, 194
método radd, 167 letras, 113
radianes, 18 rotación, carta, 80
radix sort, 201 depuración de patitos de goma, 134
rage, 200 ritmo de ejecución, 8, 15, 160
declaración de aumento, 107, 112, 159 ejecutando Python, 2
Ramanujan, Srinivasa, 70 función errores de tiempo de ejecución, 13, 44, 46, 193, 196
aleatoria, 101, 126 función aleatoria, Error de tiempo de ejecución, 45, 58
126 módulo aleatorio, 101,
126, 175 número aleatorio, 126 texto lenguaje seguro, 13
aleatorio, 131 control de cordura, 111
andamios, 53, 60, 112
Machine Translated by Google
220 Índice
dispersión, 118, 123, 191 soporte ondulado,
Schmidt, Eric, 201 clasificación
Scrabble, 123 estable 103, diagrama de pila 205, diagrama de
guión, 11, 14 estado 23, 26, 37, 44, 56, 60, 97, 9, 14, 63, 78, 90, 96, 108, 120,
modo guión, 11, 14, 24 148, 150, 152, 155, 173
búsqueda, 107, 205, 209 declaración, 10, 14
patrón de búsqueda, 75, 79, 85, 186 afirmación, 159,
búsqueda, binario, 101 160 asignación, 9, 63
búsqueda, bisección, 101 descanso,
auto (nombre del parámetro), 163 66 compuesto,
error semántico, 13, 14, 193, 198 41 condicional, 41, 47, 55, 184
semántica, 14, 162 para, 30, 72, 91
secuencia, 4, 71, 79, 89, 94, 115, 121 global, 110, 112 si,
conjunto, 130, 41
186 anagrama, 123, importar, 26, 144
145 conjunto de pasar, 41
pertenencia, 113 imprimir, 3, 7, 165, 197
conjunto resta, subir, 107, 112, 159
186 conjunto predeterminado, volver, 44, 51, 199
189 método intentar, 140,
predeterminado, 113 153
sexagesimal, mientras, 64 tamaño de paso, 79
158 copia superficial, StopIteration, 185
152, 153 forma, función str, 18
123 error de forma, 122 método __str__, 165, 174
shell, 142, 145 módulo cadena, 4, 7, 94, 121
de estantería, 142 acumulador, 175
función aleatoria, 175 función comparación, 77
sinusoidal, vacío, 95
18 singleton, inmutable, 74
108, método, 75
112, 115 multilínea, 35, 194
rebanada, operación, 12
79 copia, 74, rebanada,
92 lista, 91 cadena, 73 tupla, 116 73 triple citado, 35
actualización, 92 operador de concatenación de cadenas,
204 método de
segmento, 73, 79, 91, 98, cadena, 79 métodos de
116 método de cadena, 204 módulo
ordenación, 92, 99, de cadena, 125 representación de
176 función ordenada, cadena, 144,
165
99, 106 función ordenada, 121 clasificación, 204, 205 caso tipo de
especial, cadena,
87, 4 especial
157 valor
falso, 40 método de tira, 84, 125 módulo
Ninguno, 24, 26, 52, 92, 94 structshape,
Verdadero, 122 estructura, 5
40 espiral, sujeto, 163,
38 método dividido, 95, 169
117 sqrt, subconjunto, 187
53 función sqrt, 18 diccionario de restas,
raíz cuadrada, 66 129 con préstamo, 68 resta con préstamo, 159
Machine Translated by Google
Índice 221
sufijo, 131 Tesis de Turing, 55
traje, 171 Turing, Alan, 55
suma, 185 módulo tortuga, 48
función de suma, 118 máquina de escribir tortuga,
depuración supersticiosa, 200 patrón 37 tipo, 4, 7
de intercambio, 116 bool, 40
sintaxis, 5, 7, 13, 162, 194 error dict, 103
de sintaxis, 13, 14, 193 archivo,
Error de sintaxis, 19 137 float,
4 función, 20
variable temporal, 51, 60, 199 caso de int, 4
prueba, mínimo, 198 pruebas lista, 89
y NoneType, 24
ausencia de errores, 87 desarrollo definido por el programador, 147, 153, 155, 162, 165,
incremental, 52 es difícil, 87 saber la 173 set,
respuesta, 53 130 str, 4
salto de fe, 57 caso de prueba tuple,
mínimo, 198 115
verificación de tipos, 58
texto conversión de tipos, 17
simple, 83, 125 función de tipos, 153
aleatorio, 131 despacho basado en tipos, 166, 167, 169
archivo de texto, 145 TypeError, 72, 74, 108, 116, 118, 139, 164, 197 máquina de
Clase de tiempo, escribir, tortuga, 37 error
155 módulo de tiempo, tipográfico, 134
101 token, 5,
7 rastreo, 24, 26, 44, 46, 107, 196 método UnboundLocalError, 110 carácter
de traducción, 125 transversal, de subrayado, 10 singularidad,
72, 75, 77, 79, 85, 93, 100, 105, 106, 119, 127 diccionario, 101
168 lista, comando Unix ls, 142
91 travesía
actualizar, 64, 67, 69
base de datos,
diccionario, 120 141 variable global, 110
triángulo, 48 histograma, 127
función trigonométrica, 18 cadena elemento,
entre comillas triples, 35 91
Valor especial verdadero, 40 segmento, 92 método de
declaración de prueba, 140, actualización, 120 operador
153 tupla, 115, 117, 121, 122 de actualización, 93 usar antes de definición, 20
como clave en diccionario, 120, 132
asignación, 116 valor, 4, 7, 95, 96, 112
comparación, 116, 174 entre predeterminado,
paréntesis, 120 129 tupla, 117
singleton, 115 ValueError, 46, 117
división, método de valores, 104
116 asignación de tupla, 117 , 119, 122 variable, 9, 14
función de tupla, 115 global, 110
métodos de tupla, 204 local, 22
Lenguaje completo de Turing, 55 temporal, 51, 60, 199
Machine Translated by Google
222 Índice
actualización,
64 tupla de argumento de longitud variable,
118 barniz, 175,
180 función de vacío, 24,
26 método de vacío,
92 vorpal, 55
caminar, directorio, 140
while loop, 64
espacios en blanco, 46, 84, 144,
194 recuento de
palabras, 143 frecuencia de
palabras, 125, 134 palabras,
reducible, 113, 124 directorio
de trabajo, 139
peor error, 170 peor de los casos, 202, 209
cero, índice que comienza en,
71 cero, índice que comienza
en, 90 función zip,
118 usar con dict, 120
objeto zip, 123
ley de Zipf, 134
El principio de sustitución de Liskov establece que las instancias de clases derivadas deben poder reemplazar las de la clase base sin alterar el correcto funcionamiento del programa. Es crucial para la herencia, permitiendo que las clases secundarias extiendan y modifiquen comportamientos de manera coherente y predecible .
El "salto de fe" es un concepto de lectura de programas que implica asumir que una función funciona correctamente y devuelve el resultado correcto sin seguir todo el flujo detallado de ejecución. Se aplica en funciones recursivas al confiar en que las llamadas recursivas funcionan correctamente, permitiendo así centrarse en el resultado final sin enredarse en la complejidad del flujo de ejecución .
Dividir programas en funciones más pequeñas crea puntos de control naturales que hacen el flujo de ejecución más visible y manejable. Esto permite realizar pruebas específicas de partes del código, mejorando la identificación y corrección de errores .
El patrón "map" aplica una función a cada elemento en una secuencia, transformando la lista. "Filter" recorre la lista y selecciona elementos que cumplen con un criterio definido. "Reduce" combina elementos de una lista en un único valor, siendo útil para acumulaciones como sumas o productos .
Una copia superficial copia el objeto y sus referencias, pero no los objetos embebidos, lo cual es suficiente cuando no se precisa modificar objetos internos. Por otro lado, una copia profunda copia el objeto y todos los objetos a los que éste hace referencia, siendo preferible cuando se requiere modificar tales objetos sin alterar el original .
Para resolver problemas de recursión infinita, se recomienda asegurarse de que la función tenga un caso base claro y añadir declaraciones de impresión para rastrear los parámetros y el flujo de ejecución. Esto ayuda a identificar por qué el caso base no se alcanza. Si los parámetros no cambian adecuadamente para alcanzar el caso base, es necesario repensar el algoritmo .
Las funciones guardián utilizan condicionales para proteger el código de valores que podrían causar errores. Actúan como una primera línea de defensa, descartando inputs incorrectos antes de procesarlos, lo cual facilita la identificación y corrección de errores en una etapa temprana del flujo de ejecución .
Las clases secundarias heredan atributos y métodos de las clases principales, permitiendo la reutilización de código mientras se especializan en comportamientos específicos. La encapsulación permite que los detalles de implementación se oculten en la clase principal, garantizando que las clases secundarias cumplan con las interfaces definidas, manteniendo así la integridad del modelo de objetos .
Las condiciones previas son requisitos que deben cumplirse antes de ejecutar una función, mientras que las condiciones posteriores aseguran que la función proporcione resultados válidos. Se verifica mediante declaraciones al inicio y fin de la función, por ejemplo, confirmando tipos de datos o rangos numéricos válidos .
La programación orientada a objetos organiza código y datos mediante la creación de tipos definidos por el programador, o clases, que representan entidades del mundo real. Esto permite modularidad, reutilización de código, y una representación más intuitiva de problemas complejos al encapsular estado y comportamiento en objetos .









