• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Todo Sobre El Lenguaje Python
 

Todo Sobre El Lenguaje Python

on

  • 4,207 views

Aprende con este fabuloso manual de python, rapido y facil, y ademas muy potente

Aprende con este fabuloso manual de python, rapido y facil, y ademas muy potente

Statistics

Views

Total Views
4,207
Views on SlideShare
4,206
Embed Views
1

Actions

Likes
0
Downloads
92
Comments
0

1 Embed 1

https://www.facebook.com 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Todo Sobre El Lenguaje Python Todo Sobre El Lenguaje Python Document Transcript

    • Aprenda a Pensar Como un Programador con Python
    • Aprenda a Pensar Como un Programador con Python Allen Downey Jeffrey Elkner Chris Meyers Traducido por ´ Miguel Angel Vilella ´ Angel Arnal Iv´n Juanes a Litza Amurrio Efrain Andia C´sar Ballardini e Green Tea Press Wellesley, Massachusetts
    • Copyright c 2002 Allen Downey, Jeffrey Elkner, y Chris Meyers.Corregido por Shannon Turlington y Lisa Cutler.Dise˜o de la cubierta por Rebecca Gimenez. nGreen Tea Press1 Grove St.P.O. Box 812901Wellesley, MA 02482Se permite copiar, distribuir, y/o modificar este documento bajo los t´rminos de ela GNU Free Documentation License, Versi´n 1.1 o cualquier versi´n posterior o opublicada por la Free Software Foundation; siendo las Secciones Invariantes“Pr´logo”, “Prefacio”, y “Lista de Colaboradores”, sin texto de cubierta, y osin texto de contracubierta. Se incluye una copia de la licencia en el ap´ndice etitulado “GNU Free Documentation License”.La GNU Free Documentation License est´ disponible en www.gnu.org o escri- abiendo a la Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,MA 02111-1307, USA.La forma original de este libro es c´digo fuente L TEX. La compilaci´n de este o A ofuente L TE A X tiene el efecto de generar una representaci´n independiente del odispositivo de un libro de texto, que puede convertirse a otros formatos e im-primirse.El fuente L TEX de este libro y m´s informaci´n sobre el proyecto de Libro de A a oTexto de C´digo Abierto est´n disponibles en o a http://www.thinkpython.comLa composici´n de este libro se realiz´ utilizando L TEX y LyX. Las ilustraciones o o Ase hicieron con xfig. Todos ellos son programas gratuitos de c´digo abierto. oHistoria de la impresi´n: oAbril 2002: Primera edici´n. oISBN 0-9716775-0-6
    • Pr´logo oPor David BeazleyComo educador, investigador, y autor de libros, estoy encantado de ver la fi-nalizaci´n de este libro. Python es un lenguaje de programaci´n divertido y o oextremadamente f´cil de usar que en los ultimos a˜os se ha hecho muy popu- a ´ nlar. Desarrollado hace diez a˜os por Guido van Rossum, su sintaxis simple y la nsensaci´n general se deriva en gran parte del ABC, un lenguaje desarrollado en olos 1980s para la ense˜anza. Sin embargo, Python tambi´n se cre´ para resolver n e oproblemas reales y presenta una variedad amplia de caracter´ ısticas de lenguajesde programaci´n como C++, Java, Modula-3 y Scheme. Debido a esto, una de olas caracter´ ısticas notables de Python es su atractivo para los desarrolladoresprofesionales de progamaci´n, cient´ o ıficos, investigadores, artistas, y educadores.A pesar del atractivo de Python para muchas comunidades diferentes, puedeque a´n se pregunte “¿por qu´ Python?” o “¿por qu´ ense˜ar programaci´n u e e n ocon Python?”No es tarea f´cil responder a estas preguntas, en especial cuando ala opini´n popular est´ del lado de alternativas m´s masoquistas como C++ y o a aJava. Sin embargo, pienso que la respuesta m´s directa es que la progrmaci´n a oen Python es simplemente m´s divertida y m´s productiva. a aCuando imparto cursos de inform´tica, quiero cubrir conceptos importantes, ahacer el material interesante y enganchar a los estudiantes. Desgraciadamente,hay una tendencia en los cursos de introducci´n a la programaci´n a prestar o odemasiada atenci´n a la abstracci´n matem´tica que hace que los estudiantes o o ase frustren con problemas farragosos relacionados con detalles nimios de la sin-taxis, compilaci´n, y la aplicaci´n de reglas aparentemente arcanas. Aunque o otal abstraci´n y formalismo son importantes para ingenieros profesionales de la oprogramaci´n y estudiantes que planean continuar sus estudios de inform´tica, o adecidirse por este enfoque en un curso introductorio s´lo tiene ´xito en hacer o eaburrida la inform´tica. Cuando imparto un curso, no quiero tener un aula de aestudiantes sin inspiraci´n. Quisiera verlos intentando resolver problemas in- oteresantes, explorando ideas diferentes, probando enfoques no convencionales,
    • vi Pr´logo orompiendo las reglas, y aprendiendo de sus errores. Al hacerlo, no quiero perderla mitad del semestre tratando de sortear problemas con una sintaxis abstru-sa, mensajes de error del compilador incomprensibles, o los varios cientos demaneras que un programa puede generar un error de proteci´n general. oUna de las razones por las que me gusta Python es por que proporciona un equi-librio muy bueno entre lo pr´ctico y lo conceptual. Puesto que Python es un alenguaje interpretado, los principiantes pueden tomar el lenguaje y empezar ahacer cosas interesantes casi inmediato, sin perderse el los problemas de compila-ci´n y enlazado. Adem´s, Python viene con una gran biblioteca de m´dulos que o a ose pueden usar para hacer toda clase de tareas que abarcan desde programaci´n opara web a gr´ficos. Este enfoque pr´ctico es una buena manera de enganchar a a aestudiantes y permite que completen proyectos significativos. Sin embargo, Pyt-hon tambi´n puede servir como una base excelente para intruducir conceptos eimportantes de inform´tica. Puesto que Python soporta completamente proce- adimientos y clases, los estudiantes pueden introducirse gradualmente en temascomo abstracci´n procedural, estructuras de datos, y programaci´n orientada a oobjetos, que son aplicables a cursos posteriores en Java o C++. Python inclusotoma prestada cierta cantidad de caracter´ ısticas de lenguajes de programaci´n ofuncionales y puede usarse para intruducir conceptos que pudieran ser cubiertosen mas detalle en cursos de Scheme o Lisp.Leendo, el prefacio de Jeffrey, me sorprenden sus comentarios sobre que Pyt-hon le permite ver un “m´s alto nivel de ´xito y un bajo nivel de frustraci´n a e o 2que puede “avanzar r´pido con mejores resultados”. Aunque estos comentarios ase refieren a sus cursos introductorios, a veces uso Python por estas mismasrazones en cursos de inform´tica avanzada en la Universidad de Chicago. En aestos cursos me enfrento constantemente con la desalentadora tarea de cubrirun mont´n de material dif´ en un agotador trimestre de nueve semanas. Aun- o ıcilque es ciertamente posible para m´ infligir mucho dolor y sufrimiento usando un ılenguaje como C++, he visto a menudo que ese estilo es ineficaz, especialmentecuando el curso se trata de un asunto sin relaci´n apenas con la “programaci´n”. o oEncuentro que usar Python me permite dedicarme m´s al asunto en cuesti´n a omientras permito a los estudiantes completar proyectos utiles. ´Aunque Python es todav´ un lenguaje joven y en desarollo, creo que tiene ıaun futuro brillante en la educaci´n. Este libro es un paso importante en esa odirecci´n. oDavid BeazleyUniversidad de ChicagoAutor de Python Essential Reference
    • PrefacioPor Jeff ElknerEste libro debe su existencia a la colaboraci´n hecha posible por la Internet y oal movimiento de software libre. Sus tres autores, un profesor universitario, unprofesor de instituto y un programador profesional, todav´ tienen que conocerse ıacara a cara, pero hemos sido capaces de colaborar estrechamente y hemos reci-bido la ayuda de mucha gente maravillosa que han donado su tiempo y esfuerzopara ayudar a mejorar este libro.Creemos que este libro es un testamento a los beneficios y futuras posibilidadesde este tipo de colaboraci´n, cuyo marco han establecido Richard Stallman y la oFree Software Foundation.C´mo y por qu´ vine a usar Python o eEn 1999, el examen de Advanced Placement (AP) de Ciencias de la Computa-ci´n del Claustro Escolar se realiz´ por primera vez en C++. Como en muchos o oinstitutos en todo el pa´ la decisi´n de cambiar de lenguaje tuvo un impacto ıs, odirecto sobre el curriculum de inform´tica en el Insituto de Yorktown en Ar- alington, Virgina, donde doy clase. Hasta ese momento, el lenguaje de ense˜anza nera Pascal tanto en nuestro curso de primer a˜o como en el AP. Al seguir con nla pr´ctica anterior de dar a los estudiantes dos a˜os de exposici´n al mismo a n olenguaje, tomamos la decisi´n de cambiar a C++ en el aula de primer a˜o del o ncurso 1997-98 de modo que estar´ ıamos en sinton´ con el cambio del Claustro ıaEscolar para el curso AP del a˜o siguiente. nDos a˜os m´s tarde, me convenc´ de que C++ era una mala elecci´n para iniciar n a ı oa los estudiantes en la inform´tica. Aunque es un lenguaje de programaci´n a omuy poderoso, tambi´n es extremadamente dif´ de aprender y ense˜ar. Me e ıcil nencontr´ luchando constantemente con la dif´ sintaxis de C++ y sus m´ltiples e ıcil uformas de hacer las cosas, y como consecuencia perd´ muchos estudiantes sin ıa
    • viii Prefacionecesidad. Convencido de que deb´ de haber una elecci´n mejor para el lenguaje ıa ode nuestro curso de primer a˜o, me puse a buscar una alternativa para C++. nNecesitaba un lenguaje que funcionase tanto en las m´quinas de nuestro labo- aratorio de Linux como en las plataformas Windows y Macintosh que la mayor´ ıade los estudiantes ten´ en casa. Quer´ que fuera de c´digo abierto, para que ıan ıa olos estudiantes pudieran usarlo en casa sin importar su nivel econ´mico. Quer´ o ıaun lenguaje utilizado por programadores profesionales, y que tuviera una co-munidad activa de desarrolladores a su alrededor. Ten´ que soportar tanto la ıaprogramaci´n procedural como la orientada a objetos. Y lo m´s importante, o aten´ que ser f´cil de aprender y de ense˜ar. Cuando investigu´ las opciones con ıa a n eestos obejetivos en mente, Python destac´ como el mejor candidato. oPed´ a uno de los estudiantes m´s talentosos de Yorktown, Matt Ahrens, que ı aprobase Python. En dos meses, no s´lo hab´ aprendido el lenguaje, sino que o ıaescribi´ una aplicaci´n llamada pyTicket que permit´ a nuestro personal infor- o o ıamar de problemas tecnol´gicos via Web. Sab´ que Matt no pod´ terminar una o ıa ıaaplicaci´n de tal escala en tan poco tiempo con C++, y este logro, combinado ocon la positiva valoraci´n de Python por parte de Matt, suger´ que Python era o ıala soluci´n que buscaba. oEncontrar un libro de textoUna vez decidido a usar Python tanto en mis clases de inform´tica b´sica como a aen el a˜o siguiente, el problema m´s acuciante era la falta de un libro de texto n adisponible.El contenido libre vino al rescate. Anteriormente en ese a˜o, Richard Stallman nme present´ a Allen Downey. Ambos hab´ o ıamos escrito a Richard expresandonuestro inter´s en desarrollar conenidos educativos libres. Allen ya hab´ escrito e ıaun libro de texto de inform´tica de primer a˜o, How to Think Like a Com- a nputer Scientist. Cuando le´ ese libro, supe inmediatamente que quer´ usarlo ı ıaen mi clase. Era el libro de inform´tica m´s claro y pr´ctico que hab´ visto. a a a ıaPon´ el ´nfasis en los procesos de pensamiento involucrados en la programaci´n ıa e om´s que en las caracter´ a ısticas de un lenguaje en particular. Su lectura me hizoinmediatamente un maestro mejor.How to Think Like a Computer Scientist no era s´lo un libro excelente, sino que ose public´ bajo la licencia p´blica GNU, lo que significaba que pod´ usarse y o u ıamodificarse libremente para ajustarse a las necesidades de su usuario. Una vezque decid´ usar Python, se me ocurri´ que podr´ traducir la versi´n original ı o ıa oen Java del libro de Allen al nuevo lenguaje. Aunque no hubiera sido capaz deescribir un libro de texto por mi cuenta, tener el libro de Allen para trabajar a
    • ixpartir de ´l me hizo posible hacerlo, mostrando al mismo tiempo que el modelo ecooperativo de desarrollo que tan buenos resultados hab´ dado en el software ıapod´ funcionar tambi´n para el contenido educativo. ıa eEl trabajo en este libro durante los dos ultimos a˜os ha sido gratificante para mis ´ nestudiantes y para m´ y mis estudiantes desempe˜aron un importante papel en ı, nel proceso. Como pod´ hacer cambios instant´neos cuando alguien encontraba ıa aun error ortogr´fico o un pasaje dif´ a ıcil, los anim´ a buscar errores en el libro ed´ndoles un punto extra cada vez que hac´ una sugerencia que terminaba a ıancomo un cambio en el texto. Esto tuvo el doble beneficio de animarlos a leer eltexto con m´s atenci´n y tener el texto revisado en profundidad por sus cr´ a o ıticosm´s importantes: los estudiantes que lo usan para aprender inform´tica. a aPara la segunda mitad del libro, acerca de la programaci´n orientada a objetos, osab´ que necesitar´ a alguien con m´s experiencia real en programaci´n de ıa ıa a ola que yo ten´ para hacerlo bien. El libro se estanc´ en un estado inacabado ıa odurante buena parte de un a˜o hasta que la comunidad de c´digo abierto de n onuevo proporcion´ los medios necesarios para su terminaci´n. o oRecib´ un correo electr´nico de Chris Meyers expresando su inter´s en el li- ı o ebro. Chris es un programador profesional que empez´ a impartir un curso de oprogramaci´n con Python el a˜o pasado en el Colegio de Lane Community, o nen Eugene, Oregon. La perspectiva de impartir el curso llev´ a Chris has- ota el libro, y empez´ a colaborar con ´l inmediatamente. Hacia el final del o ea˜o escolar hab´ creado un proyecto complementario en nuesto sitio web en n ıahttp://www.ibiblio.org/obp llamado Python for Fun y estaba trabajandocon algunos de mis estudiantes aventajados como profesor magistral, dirigi´ndo- eles m´s all´ de donde yo pod´ llevarles. a a ıaPresentando la programaci´n con Python oEl proceso de traducir y usar How to Think Like a Computer Scientist duran-te los dos ultimos a˜os ha confirmado la idoneidad de Python para ense˜ar a ´ n nestudiantes principiantes. Python simplifica enormemente los ejemplos de pro-gramaci´n y facilita la ense˜anza de los conceptos importantes en programaci´n. o n o
    • x PrefacioEl primer ejemplo del texto ilustra esta cuesti´n. Es el tradicional programa o“hola, mundo”, que en la versi´n C++ del libro es as´ o ı: #include <iostream.h> void main() { cout << "Hola, mundo" << endl; }en la versi´n Python se convierte en: o print "Hola, Mundo"Aunque es un ejemplo trivial, destacan las ventajas de Python. El curso deInform´tica I en Yorktown no tiene prerrequisitos, as´ que muchos de los estu- a ıdiantes que ven este ejemplo est´n mirando su primer programa. Algunos de aellos est´n sin duda un poco nerviosos, tras haber o´ que programar compu- a ıdotadores es algo dif´ de aprender. La versi´n C++ siempre me ha obligado a ıcil oelegir entre dos opciones insatisfactorias: explicar las sentencias #include, voidmain(), {, y } y arriesgarme a confundir o intimidar a algunos estudiantes desdeel principio, o decirles “No te preocupes de todo eso ahora, hablaremos de ellom´s tarde”, y arriesgarme a lo mismo. Los objetivos educativos en este momento adel curso son exponer a los estudiantes a la idea de una sentencia de progra-maci´n y llevarles a escribir su primer programa, present´ndoles de esta forma o ael entorno de programaci´n. La programaci´n con Python tiene exactamente lo o oque necesito para hacer estas cosas, y nada m´s. aLa comparaci´n del texto explicativo de este programa para cada versi´n del o olibro ilustra mejor lo que esto significa para los estudiantes principiantes. Haytrece p´rrafos de explicaci´n de “¡Hola, mundo!” en la versi´n C++. En la a o oversi´n Python s´lo hay dos. A´n m´s importante: los once p´rrafos que faltan o o u a ano tocan las “grandes ideas” de la programaci´n de computadores, sino las ominucias de la sintaxis de C++. Encontr´ que esto mismo suced´ por todo el e ıalibro. P´rrafos enteros desapareciendo de la versi´n Python del texto porque la a osintaxis clara de Python los hace innecesarios.El uso de un lenguaje de muy alto nivel como Python permite que el profesordeje para m´s tarde hablar sobre los detalles de bajo nivel de la m´quina hasta a aque los estudiantes tengan el fondo necesario para entender los detalles. De estemodo crea la habilidad de poner pedag´gicamente “antes lo primero”. Uno de olos mejores ejemplos de ello es la manera en la cual Python maneja las variables.En C++ una variable es un nombre para un lugar que contiene una cosa. Lasvariables deben declararse seg´n su tipo en parte porque el tama˜o del lugar al u nque apuntan tiene que determinarse de antemano. As´ la idea de una variable ı,est´ ligada al hardware de la m´quina. El concepto poderoso y fundamental de a a
    • xilo que es una variable ya es suficientemente dif´ para estudiantes principiantes ıcil(tanto de inform´tica como de ´lgebra). Octetos y direcciones no ayudan a la a acomprensi´n. En Python una variable es un nombre que se˜ala una cosa. Este o nes un concepto mucho m´s intuitivo para estudiantes principiantes y est´ m´s a a acerca del significado de “variable” que aprendieron en su clase de matem´ticas. aEste a˜o tuve muchas menos dificultades ense˜ando lo que son las variables que n nen el anterior, y pas´ menos tiempo ayud´ndoles con los problemas derivados e ade su uso.Otro ejemplo de c´mo Python ayuda en la ense˜anza y aprendizaje de la pro- o ngramaci´n es en su sintaxis para las funciones. Mis estudiantes siempre han otenido una gran dificultad comprendiendo las funciones. El problema principalse centra alrededor de la diferencia entre la definici´n de una funci´n y la llama- o oda a una funci´n, y la distinci´n asociada entre un par´metro y un argumento. o o aPython viene al rescate con una sintaxis a la que no le falta belleza. La defini-ci´n de una funci´n empieza con la palabra clave def, y simplemente digo a mis o oestudiantes: “cuando definas una funci´n, empieza con def, seguido del nombre ode la funci´n que est´s definiendo; cuando llames a una funci´n, simplemente di o e o(escribe) su nombre”. Los par´metros van con las definiciones; los argumentos acon las llamadas. No hay tipo de retorno, tipos de par´metros, o par´metro por a areferencia y valor de por medio, por lo que ahora soy capaz de ense˜ar funciones nen la mitad de tiempo que antes, con mejor comprensi´n. oEl uso de Python ha mejorado la eficacia de nuestro programa de inform´ticaapara todos los estudiantes. Veo un mayor nivel general de ´xito y un menor enivel de frustraci´n del que experiment´ durante los dos a˜os que ense˜´ C++. o e n neAvanzo m´s r´pido con mejores resultados. M´s estudiantes terminan el curso a a acon la habilidad de crear programas utiles y con la actitud positiva hacia la ´experiencia de programaci´n que esto engendra. oFormar una comunidadHe recibido correos electr´nicos de todos los rincones del planeta de parte ode gente que usa este libro para aprender o enese˜ar a programar. Ha em- npezando a surgir una comunidad de usuarios, y muchas personas han contri-buido al proyecto mandando materiales a trav´s del sitio web complementario ehttp://www.thinkpython.com.Con la publicaci´n de este libro en forma impresa, espero que continue y se oacelere el crecimiento de la comunidad de usuarios. La emergencia de esta co-munidad de usuarios y la posibilidad que sugiere para colaboraciones similaresentre educadores han sido para m´ las partes m´s excitantes de trabajar en este ı aproyecto. Trabajando juntos, podemos incrementar la calidad de los materiales
    • xii Prefaciodisponibles para nuestro uso y ahorrar un tiempo valioso. Les invito a unirse anuestra comunidad y espero con impaciencia saber algo de ustedes. Por favor,escriban a los autores a feedback@thinkpython.com.Jeffrey ElknerEscuela Secundaria YortownArlington, Virginia
    • Lista de ColaboradoresParafraseando la filosof´ de la Free Software Foundation, este libro es libre ıacomo la libre expresi´n, pero no necesariamente gratis como la pizza gratis. oSe hizo realidad a causa de una colaboraci´n que no habr´ sido posible sin o ıala GNU Free Documentation License. As´ que queremos agradecer a la Free ıSoftware Foundation por desarrollar esta licencia y, por supuesto, ponerla anuestra disposici´n. oTambi´n nos gustar´ dar las gracias a los m´s de cien lectores de aguda vista e ıa aque se han preocupado de enviarnos sugerencias y correcciones en los dos ulti- ´mos a˜os. Siguiendo el esp´ n ıritu del software libre, decidimos expresar nuestragratitud en la forma de una lista de colaboradores. Desgraciadamente, esta listono est´ completa, pero hacemos lo que podemos para mantenerla actualizada. aSi se toma el tiempo de echar un vistazo a la lista, ver´ que cada una de las apersonas que aparecen le ha ahorrado a usted y a los lectores que le sucedanla confusi´n de un error t´cnico o una explicaci´n poco clara simplemente en- o e ovi´ndonos una nota. aPos imposible que parezca tras tantas correcciones, todav´ puede haber ıaerrores en el libro. Si se encontrara con una, esperamos que se tome unminuto para ponerse en contacto con nosotros. La direcci´n de correo es ofeedback@thinkpython.com. Si cambiamos algo a partir de su sugerencia, apa-recer´ en la siguiente versi´n de la lista de colaboradores (a no ser que pida a oquedar omitido). ¡Gracias! Lloyd Hugh Allen envi´ una correcci´n de la Secci´n 8.4. o o o Yvon Boulianne envi´ una correcci´n de un error sem´ntico en el Cap´ o o a ıtulo 5. Fred Bremmer comunic´ una correcci´n de la Secci´n 2.1. o o o Jonah Cohen escribi´ los scripts en Perl para convertir la fuente L TEX del o A libro en hermoso HTML.
    • xiv Lista de Colaboradores Michael Conlon envi´ una correcci´n gramatical del Cap´ o o ıtulo 2 y una mejora del estilo del Cap´ ıtulo 1, e inici´ una discusi´n sobre aspectos o o t´cnicos de los int´rpretes. e e Benoit Girard envi´ una correcci´n de un divertido error de la Secci´n 5.6. o o o Courtney Gleason y Katherine Smith escribieron horsebet.py, que se us´ como un caso de estudio en una versi´n temprana del libro. Su pro- o o grama puede encontrarse en el sitio web. Lee Harr comunic´ m´s correcciones de las que tenemos sitio para enume- o a rar aqu´ y de verdad deber´ aparecer como uno de los principales editores ı, ıa del texto. James Kaylin es un estudiante que us´ el texto. Envi´ numerosas correc- o o ciones. David Kershaw arregl´ la funci´n catTwice que no funcionaba en la Sec- o o ci´n 3.10. o Eddie Lam ha enviado numerosas correcciones de los Cap´ ıtulos 1, 2 y 3. Tambi´n arregl´ el Makefile de forma que crea un ´ e o ındice la primera vez que se ejecuta y nos ayud´ a preparar un esquema de versiones. o Man-Yong Lee envi´ una correcci´n del c´digo de ejemplo de la Secci´n o o o o 2.4. David Mayo se˜al´ que la palabra “unconscientemente”en el Cap´ n o ıtulo 1 deb´ cambiarse por “subconscientemente”. ıa Chris McAloon envi´ varias correciones de las Secciones 3.9 y 3.10. o Matthew J. Moelter ha sido un colaborador durante mucho tiempo y ha enviado numerosas correcciones y sugerencias. Simon Dicon Montford inform´ de una definici´n de funci´n faltante y o o o varios errores tipogr´ficos en el Cap´ a ıtulo 3. Tambi´n encontr´ errores en e o la funci´n incrementa del Cap´ o ıtulo 13. John Ouzts corrigi´ la definici´n de “valor de retorno”del Cap´ o o ıtulo 3. Kevin Parks envi´ valiosos comentarios y sugerencias acerca de c´mo me- o o jorar la distribuci´n del libro. o David Pool envi´ un error tipogr´fico en el glosario del Cap´ o a ıtulo 1, y tambi´n amables palabras de ´nimo. e a
    • xvMichael Schmitt envi´ una correcci´n del Cap´ o o ıtulo sobre archivos y excep-ciones.Robin Shaw se˜al´ un error en la Secci´n 13.1, donde la funci´n impri- n o o omeHora se usaba en un ejemplo sin haberla definido.Paul Sleigh encontr´ un error en el Cap´ o ıtulo 7 y un error en el script Perlde Jonah Cohen que, a partir de L TEX genera, el HTML. ACraig T. Snydal est´ poniendo a prueba el texto en un curso en la Uni- aversidad de Drew. Ha contribuido con varias sugerencias y correcciones deimportancia.Ian Thomas y sus estudiantes usan el texto en un curso de programaci´n. oSon los primeros en probar los Cap´ ıtulos de la segunda mitad del libro, yhan hecho numerosas correcciones y sugerencias.Keith Verheyden envi´ una correcci´n del Cap´ o o ıtulo 3.Peter Winstanley nos hizo saber de un persistente error en nuestro lat´ ındel Cap´ ıtulo 3.Chris Wrobel hizo correcciones al c´digo del Cap´ o ıtulo sobre E/S de archi-vos y excepciones.Moshe Zadka ha hecho contribuciones inestimables al proyecto. Adem´s ade escribir el primer borrador del Cap´ ıtulo sobre diccionarios, propor-cion´ una gu´ continuada en las primeras etapas del libro. o ıaChristoph Zwerschke envi´ varias correcciones y sugerencias pedag´gicas, o oy explic´ la diferencia entre gleich y selbe. oJames Mayer envi´ un cargamento de errores tipogr´ficos y ortogr´ficos, o a aincluyendo dos en la lista de colaboradores.Hayden McAfee pill´ una inconsistencia potencialmente confusa entre dos oejemplos.´Angel Arnal es parte de un equipo internacional de traductores que tra-bajan en la versi´n en espa˜ol del texto. Tambi´n ha encontrado varios o n eerrores en la versi´n inglesa. oTauhidul Hoque y Lex Berezhny crearon las ilustraciones del Cap´ ıtulo 1y mejoraron muchas de las otras ilustraciones.Dr. Michele Alzetta pill´ un error en el Cap´ o ıtulo 8 y envi´ varios comen- otarios y sugerencias pedag´gicas interesantes sobre Fibonacci y La Mona. o
    • xvi Lista de Colaboradores Andy Mitchell pill´ un error tipogr´fico en el Cap´ o a ıtulo 1 y un ejemplo err´neo en el Cap´ o ıtulo 2. Kalin Harvey sugiri´ una clarificaci´n al Cap´ o o ıtulo 7 y detect´ varios errores o tipogr´ficos. a Christopher P. Smith encontr´ varios errores tipogr´ficos y nos est´ ayu- o a a dando a preparar la actualizaci´n del libro para Python 2.2. o David Hutchins pill´ un error tipogr´fico en el Pr´logo. o a o Gregor Lingl ense˜a Python en un instituto de Viena, Austria. Est´ tra- n a bajando en una traducci´n del libro al alem´n, y pill´ un par de errores o a o graves en el Cap´ ıtulo 5. Julie Peters encontr´ un error tipogr´fico en el Prefacio. o a
    • ´Indice generalPr´logo o vPrefacio viiLista de Colaboradores xiii1. El Camino del Programa 1 1.1. El lenguaje de programaci´n Python . . . . . . . . . . . . . . . o 1 1.2. ¿Qu´ es un programa? . . . . . . . . . . . . . . . . . . . . . . . e 3 1.3. ¿Qu´ es la depuraci´n (debugging)? . . . . . . . . . . . . . . . . e o 4 1.4. Lenguajes formales y lenguajes naturales . . . . . . . . . . . . . 6 1.5. El primer programa . . . . . . . . . . . . . . . . . . . . . . . . . 8 1.6. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92. Variables, expresiones y sentencias 11 2.1. Valores y tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.2. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.3. Nombres de variables y palabras reservadas . . . . . . . . . . . 13 2.4. Sentencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.5. Evaluar expresiones . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.6. Operadores y expresiones . . . . . . . . . . . . . . . . . . . . . 16
    • xviii ´ Indice general 2.7. El orden de las operaciones . . . . . . . . . . . . . . . . . . . . 17 2.8. Las operaciones sobre cadenas . . . . . . . . . . . . . . . . . . . 17 2.9. Composici´n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . o 18 2.10. Los comentarios . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.11. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193. Funciones 21 3.1. Llamadas a funciones . . . . . . . . . . . . . . . . . . . . . . . . 21 3.2. Conversi´n de tipos . . . . . . . . . . . . . . . . . . . . . . . . . o 22 3.3. Coerci´n de tipos . . . . . . . . . . . . . . . . . . . . . . . . . . o 22 3.4. Funciones matem´ticas . . . . . . . . . . . . . . . . . . . . . . . a 23 3.5. Composici´n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . o 24 3.6. A˜adir funciones nuevas . . . . . . . . . . . . . . . . . . . . . . n 24 3.7. Las definiciones y el uso . . . . . . . . . . . . . . . . . . . . . . 26 3.8. Flujo de ejecuci´n . . . . . . . . . . . . . . . . . . . . . . . . . . o 27 3.9. Par´metros y argumentos . . . . . . . . . . . . . . . . . . . . . a 28 3.10. Las variables y los par´metros son locales . . . . . . . . . . . . a 29 3.11. Diagramas de pila . . . . . . . . . . . . . . . . . . . . . . . . . . 30 3.12. Funciones con resultado . . . . . . . . . . . . . . . . . . . . . . 31 3.13. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324. Condicionales y recursividad 35 4.1. El operador m´dulo . . . . . . . . . . . . . . . . . . . . . . . . . o 35 4.2. Expresiones booleanas . . . . . . . . . . . . . . . . . . . . . . . 36 4.3. Operadores l´gicos . . . . . . . . . . . . . . . . . . . . . . . . . o 36 4.4. Ejecuci´n condicional . . . . . . . . . . . . . . . . . . . . . . . . o 37 4.5. Ejecuci´n alternativa . . . . . . . . . . . . . . . . . . . . . . . . o 37 4.6. Condiciones encadenadas . . . . . . . . . . . . . . . . . . . . . . 38
    • ´Indice general xix 4.7. Condiciones anidadas . . . . . . . . . . . . . . . . . . . . . . . . 39 4.8. La sentencia return . . . . . . . . . . . . . . . . . . . . . . . . 40 4.9. Recursividad . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 4.10. Diagramas de pila para funciones recursivas . . . . . . . . . . . 42 4.11. Recursividad infinita . . . . . . . . . . . . . . . . . . . . . . . . 43 4.12. Entrada por teclado . . . . . . . . . . . . . . . . . . . . . . . . 44 4.13. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455. Funciones productivas 47 5.1. Valores de retorno . . . . . . . . . . . . . . . . . . . . . . . . . 47 5.2. Desarrollo de programas . . . . . . . . . . . . . . . . . . . . . . 48 5.3. Composici´n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . o 51 5.4. Funciones booleanas . . . . . . . . . . . . . . . . . . . . . . . . 52 5.5. M´s recursividad . . . . . . . . . . . . . . . . . . . . . . . . . . a 53 5.6. Acto de fe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 5.7. Un ejemplo m´s . . . . . . . . . . . . . . . . . . . . . . . . . . . a 56 5.8. Comprobaci´n de tipos . . . . . . . . . . . . . . . . . . . . . . . o 57 5.9. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586. Iteraci´n o 61 6.1. Asignaci´n m´ltiple . . . . . . . . . . . . . . . . . . . . . . . . . o u 61 6.2. La sentencia while . . . . . . . . . . . . . . . . . . . . . . . . . 62 6.3. Tablas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 6.4. Tablas de dos dimensiones . . . . . . . . . . . . . . . . . . . . . 66 6.5. Encapsulado y generalizaci´n . . . . . . . . . . . . . . . . . . . o 67 6.6. M´s encapsulaci´n . . . . . . . . . . . . . . . . . . . . . . . . . a o 68 6.7. Variables locales . . . . . . . . . . . . . . . . . . . . . . . . . . 69 6.8. M´s generalizaci´n . . . . . . . . . . . . . . . . . . . . . . . . . a o 70 6.9. Funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 6.10. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
    • xx ´ Indice general7. Cadenas 75 7.1. Un tipo de datos compuesto . . . . . . . . . . . . . . . . . . . . 75 7.2. Longitud . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 7.3. Recorrido y el bucle for . . . . . . . . . . . . . . . . . . . . . . 76 7.4. Porciones de cadenas . . . . . . . . . . . . . . . . . . . . . . . . 78 7.5. Comparaci´n de cadenas . . . . . . . . . . . . . . . . . . . . . . o 78 7.6. Las cadenas son inmutables . . . . . . . . . . . . . . . . . . . . 79 7.7. Una funci´n “encuentra” . . . . . . . . . . . . . . . . . . . . . . o 80 7.8. Bucles y conteo . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 7.9. El m´dulo “string” . . . . . . . . . . . . . . . . . . . . . . . . . o 81 7.10. Clasificaci´n de caracteres . . . . . . . . . . . . . . . . . . . . . o 82 7.11. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 838. Listas 85 8.1. Valores de una lista . . . . . . . . . . . . . . . . . . . . . . . . . 85 8.2. Acceso a los elementos . . . . . . . . . . . . . . . . . . . . . . . 86 8.3. Longitud (tama˜o) de una lista . . . . . . . . . . . . . . . . . . n 87 8.4. Pertenencia a una lista . . . . . . . . . . . . . . . . . . . . . . . 88 8.5. Listas y bucles for . . . . . . . . . . . . . . . . . . . . . . . . . 88 8.6. Operaciones con listas . . . . . . . . . . . . . . . . . . . . . . . 89 8.7. Porciones (slices) . . . . . . . . . . . . . . . . . . . . . . . . . . 90 8.8. Las listas son mutables . . . . . . . . . . . . . . . . . . . . . . . 90 8.9. Borrado en una lista . . . . . . . . . . . . . . . . . . . . . . . . 91 8.10. Objetos y valores . . . . . . . . . . . . . . . . . . . . . . . . . . 91 8.11. Alias (poner sobrenombres) . . . . . . . . . . . . . . . . . . . . 92 8.12. Clonar listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 8.13. Listas como par´meteros . . . . . . . . . . . . . . . . . . . . . . a 94 8.14. Listas anidadas . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
    • ´Indice general xxi 8.15. Matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 8.16. Cadenas y listas . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 8.17. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 979. Tuplas 99 9.1. Mutabilidad y tuplas . . . . . . . . . . . . . . . . . . . . . . . . 99 9.2. Asignaci´n de tuplas . . . . . . . . . . . . . . . . . . . . . . . . 100 o 9.3. Tuplas como valor de retorno . . . . . . . . . . . . . . . . . . . 101 9.4. N´meros aleatorios . . . . . . . . . . . . . . . . . . . . . . . . . 101 u 9.5. Lista de n´meros aleatorios . . . . . . . . . . . . . . . . . . . . 102 u 9.6. Conteo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 9.7. Muchos baldes . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 9.8. Una soluci´n en una sola pasada . . . . . . . . . . . . . . . . . 106 o 9.9. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10710.Diccionarios 109 10.1. Operaciones sobre diccionarios . . . . . . . . . . . . . . . . . . . 110 10.2. M´todos del diccionario . . . . . . . . . . . . . . . . . . . . . . 111 e 10.3. Asignaci´n de alias y copiado . . . . . . . . . . . . . . . . . . . 112 o 10.4. Matrices dispersas . . . . . . . . . . . . . . . . . . . . . . . . . 112 10.5. Pistas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 10.6. Enteros largos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 10.7. Contar letras . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 10.8. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11611.Archivos y excepciones 119 11.1. Archivos de texto . . . . . . . . . . . . . . . . . . . . . . . . . . 121 11.2. Escribir variables . . . . . . . . . . . . . . . . . . . . . . . . . . 123 11.3. Directorios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
    • xxii ´ Indice general 11.4. Encurtido . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 11.5. Excepciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126 11.6. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12812.Clases y objetos 131 12.1. Tipos compuestos definidos por el usuario . . . . . . . . . . . . 131 12.2. Atributos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 12.3. Instancias como par´metro . . . . . . . . . . . . . . . . . . . . . 133 a 12.4. Mismidad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 12.5. Rect´ngulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 a 12.6. Instancias como valores de retorno . . . . . . . . . . . . . . . . 136 12.7. Los objetos son mudables . . . . . . . . . . . . . . . . . . . . . 136 12.8. Copiado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 12.9. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13913.Clases y funciones 141 13.1. Hora . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 13.2. Funciones puras . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 13.3. Modificadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 13.4. ¿Qu´ es mejor? . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 e 13.5. Desarrollo de prototipos frente a planificaci´n . . . . . . . . . . 145 o 13.6. Generalizaci´n o . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 13.7. Algoritmos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 13.8. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14714.Clases y m´todos e 149 14.1. Caracter´ ısticas de la orientaci´n a objetos . . . . . . . . . . . . 149 o 14.2. imprimeHora . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 14.3. Otro ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
    • ´Indice general xxiii 14.4. Un ejemplo m´s complicado . . . . . . . . . . . . . . . . . . . . 152 a 14.5. Argumentos opcionales . . . . . . . . . . . . . . . . . . . . . . . 153 14.6. El m´todo de inicializaci´n . . . . . . . . . . . . . . . . . . . . . 154 e o 14.7. Revisi´n de los Puntos . . . . . . . . . . . . . . . . . . . . . . . 155 o 14.8. Sobrecarga de operadores . . . . . . . . . . . . . . . . . . . . . 156 14.9. Polimorfismo . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158 14.10. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16015.Conjuntos de objetos 161 15.1. Composici´n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 o 15.2. Objetos Carta . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 15.3. Atributos de clase y el m´todo e str . . . . . . . . . . . . . . 163 15.4. Comparaci´n de naipes . . . . . . . . . . . . . . . . . . . . . . . 164 o 15.5. Mazos de naipes . . . . . . . . . . . . . . . . . . . . . . . . . . 165 15.6. Impresi´n del mazo de naipes . . . . . . . . . . . . . . . . . . . 166 o 15.7. Barajar el mazo . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 15.8. Eliminaci´n y reparto de los naipes . . . . . . . . . . . . . . . . 168 o 15.9. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16916.Herencia 171 16.1. Herencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 16.2. Una mano de cartas . . . . . . . . . . . . . . . . . . . . . . . . 172 16.3. El reparto de los naipes . . . . . . . . . . . . . . . . . . . . . . 173 16.4. Mostremos la mano . . . . . . . . . . . . . . . . . . . . . . . . . 174 16.5. La clase JuegoDeCartas . . . . . . . . . . . . . . . . . . . . . . 175 16.6. La clase ManoDeLaMona . . . . . . . . . . . . . . . . . . . . . . . 176 16.7. La clase JuegoDeLaMona . . . . . . . . . . . . . . . . . . . . . . 177 16.8. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
    • xxiv ´ Indice general17.Listas enlazadas 183 17.1. Referencias incrustadas . . . . . . . . . . . . . . . . . . . . . . . 183 17.2. La clase Nodo . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183 17.3. Listas como colecciones . . . . . . . . . . . . . . . . . . . . . . . 185 17.4. Listas y recursividad . . . . . . . . . . . . . . . . . . . . . . . . 186 17.5. Listas infinitas . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 17.6. Teorema fundamental de la ambig¨edad . . . . . . . . . . . . . 188 u 17.7. Modificar listas . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 17.8. Envoltorios y ayudantes . . . . . . . . . . . . . . . . . . . . . . 190 17.9. La clase ListaEnlazada . . . . . . . . . . . . . . . . . . . . . . 190 17.10. Invariantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 17.11. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19218.Pilas 195 18.1. Tipos abstractos de datos . . . . . . . . . . . . . . . . . . . . . 195 18.2. El TAD Pila . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 18.3. C´mo implementar pilas con listas de Python . . . . . . . . . . 196 o 18.4. Uso de push y pop . . . . . . . . . . . . . . . . . . . . . . . . . 197 18.5. Usar una pila para evaluar postfijo . . . . . . . . . . . . . . . . 198 18.6. An´lisis sint´ctico . . . . . . . . . . . . . . . . . . . . . . . . . . 199 a a 18.7. Evaluar un postfijo . . . . . . . . . . . . . . . . . . . . . . . . . 199 18.8. Clientes y proveedores . . . . . . . . . . . . . . . . . . . . . . . 200 18.9. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20119.Colas 203 19.1. El TAD Cola . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203 19.2. Cola Enlazada . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204 19.3. Rendimiento t´ ıpico . . . . . . . . . . . . . . . . . . . . . . . . . 205
    • ´Indice general xxv 19.4. Cola Enlazada Mejorada . . . . . . . . . . . . . . . . . . . . . . 205 19.5. Cola priorizada . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 19.6. La clase Golfista . . . . . . . . . . . . . . . . . . . . . . . . . 209 19.7. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210 ´20. Arboles 211 20.1. Crear ´rboles . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212 a 20.2. Recorrer ´rboles a . . . . . . . . . . . . . . . . . . . . . . . . . . 213 ´ 20.3. Arboles de expresi´n . . . . . . . . . . . . . . . . . . . . . . . . 213 o 20.4. Recorrido de un ´rbol . . . . . . . . . . . . . . . . . . . . . . . 214 a 20.5. Construir un ´rbol de expresi´n . . . . . . . . . . . . . . . . . . 216 a o 20.6. Manejar errores . . . . . . . . . . . . . . . . . . . . . . . . . . . 220 20.7. El ´rbol de animales . . . . . . . . . . . . . . . . . . . . . . . . 221 a 20.8. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224A. Depuraci´n o 225 A.1. Errores de sintaxis . . . . . . . . . . . . . . . . . . . . . . . . . 225 A.2. Errores en tiempo de ejecuci´n . . . . . . . . . . . . . . . . . . 227 o A.3. Errores sem´nticos . . . . . . . . . . . . . . . . . . . . . . . . . 231 aB. Crear un nuevo tipo de datos 235 B.1. Multiplicaci´n de fracciones . . . . . . . . . . . . . . . . . . . . 236 o B.2. Suma de fracciones . . . . . . . . . . . . . . . . . . . . . . . . . 237 B.3. Algoritmo de Euclides . . . . . . . . . . . . . . . . . . . . . . . 238 B.4. Comparar fracciones . . . . . . . . . . . . . . . . . . . . . . . . 239 B.5. Forzando la m´quina . . . . . . . . . . . . . . . . . . . . . . . . 240 a B.6. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
    • xxvi ´ Indice generalC. Listados Completos de Python 243 C.1. Clase Punto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243 C.2. Clase Hora . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244 C.3. Cartas, mazos y juegos . . . . . . . . . . . . . . . . . . . . . . . 245 C.4. Lists Enlazadas . . . . . . . . . . . . . . . . . . . . . . . . . . . 249 C.5. Clase Pila . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250 C.6. Colas y colas priorizadas . . . . . . . . . . . . . . . . . . . . . . 251 C.7. ´ Arboles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253 C.8. ´ Arboles de expresi´n . . . . . . . . . . . . . . . . . . . . . . . . 254 o C.9. Adivina el animal . . . . . . . . . . . . . . . . . . . . . . . . . . 255 C.10. Fraction class . . . . . . . . . . . . . . . . . . . . . . . . . . . 256D. Lecturas recomendadas 259 D.1. Libros y sitios web sobre Python . . . . . . . . . . . . . . . . . 260 D.2. Libros recomendados sobre inform´tica en general . . . . . . . . 261 aE. GNU Free Documentation License 263 E.1. Applicability and Definitions . . . . . . . . . . . . . . . . . . . 264 E.2. Verbatim Copying . . . . . . . . . . . . . . . . . . . . . . . . . 265 E.3. Copying in Quantity . . . . . . . . . . . . . . . . . . . . . . . . 265 E.4. Modifications . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266 E.5. Combining Documents . . . . . . . . . . . . . . . . . . . . . . . 268 E.6. Collections of Documents . . . . . . . . . . . . . . . . . . . . . 269 E.7. Aggregation with Independent Works . . . . . . . . . . . . . . . 269 E.8. Translation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269 E.9. Termination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270 E.10. Future Revisions of This License . . . . . . . . . . . . . . . . . 270 E.11. Addendum: How to Use This License for Your Documents . . . 270
    • Cap´ ıtulo 1El Camino del ProgramaEl objetivo de este libro es ense˜arle a pensar como lo hacen los cient´ n ıficosinform´ticos. Esta manera de pensar combina las mejores caracter´ a ısticas de lamatem´tica, la ingenier´ y las ciencias naturales. Como los matem´ticos, los a ıa, acient´ ıficos inform´ticos usan lenguajes formales para designar ideas (espec´ a ıfica-mente, computaciones). Como los ingenieros, ellos dise˜an cosas, ensamblando nsistemas a partir de componentes y evaluando ventajas y desventajas de cadauna de las alternativas. Como los cient´ ıficos, ellos observan el comportamientode sistemas complejos, forman hip´tesis, y prueban sus predicciones. oLa habilidad m´s importante del cient´ a ıfico inform´tico es la soluci´n de pro- a oblemas. La soluci´n de problemas incluye poder formular problemas, pensar en ola soluci´n de manera creativa, y expresar una soluci´n con claridad y precisi´n. o o oComo se ver´, el proceso de aprender a programar es la oportunidad perfecta apara desarrollar la habilidad de resolver problemas. Por esa raz´n este cap´ o ıtulose llama “El Camino del programa”.A cierto nivel, usted aprender´ a programar, lo cual es una habilidad muy util a ´por s´ misma. A otro nivel, usted utilizar´ la programaci´n para obtener alg´n ı a o uresultado. Ese resultado se ver´ m´s claramente durante el proceso. a a1.1. El lenguaje de programaci´n Python oEl lenguaje de programaci´n que aprender´ es Python. Python es un ejemplar o ade un lenguaje de alto nivel; otros ejemplos de lenguajes de alto nivel son C,C++, Perl y Java.
    • 2 El Camino del ProgramaComo se puede deducir de la nomenclatura “lenguaje de alto nivel”, tambi´n eexisten lenguajes de bajo nivel, a los que tambi´n se califica como lengua- ejes de m´quina o lenguajes ensambladores. A prop´sito, los computadores s´lo a o oejecutan programas escritos en lenguajes de bajo nivel. Los programas de altonivel tienen que traducirse antes de ejecutarse. Esta traducci´n lleva tiempo, lo ocual es una peque˜a desventaja de los lenguajes de alto nivel. nAun as´ las ventajas son enormes. En primer lugar, la programaci´n en lenguajes ı ode alto nivel es mucho m´s f´cil; escribir programas en un lenguaje de alto nivel a atoma menos tiempo, los programas son m´s cortos y m´s f´ciles de leer, y es m´s a a a aprobable que estos programas sean correctos. En segundo lugar, los lenguajesde alto nivel son portables, lo que significa que pueden ejecutarse en tiposdiferentes de computadores sin modificaci´n alguna o con pocas modificaciones. oLos programas escritos en lenguajes de bajo nivel s´lo pueden ser ejecutarse en oun tipo de computador y deben reescribirse para ejecutarlos en otro.Debido a estas ventajas, casi todos los programa se escriben en un lenguaje dealto nivel. Los lenguajes de bajo nivel s´lo se usan para unas pocas aplicaciones oespeciales.Hay dos tipos de programas que traducen lenguajes de alto nivel a lenguajesde bajo nivel: int´rpretes y compiladores. Un int´rprete lee un programa de e ealto nivel y lo ejecuta, lo que significa que lleva a cabo lo que indica el programa.Traduce el programa poco a poco, leyendo y ejecutando cada comando. CODIGO INTERPRETER SALIDA FUENTEUn compilador lee el programa y lo traduce todo al mismo tiempo, antes deejecutar cualquiera de las instrucciones. En este caso, al programa de alto nivelse le llama el c´digo fuente, y al programa traducido el c´digo de obje- o oto o el c´digo ejecutable. Una vez compilado el programa, puede ejecutarlo orepetidamente sin volver a traducirlo.CODIGO CODIGO COMPILADOR EJECUTOR SALIDAFUENTE OBJETOPython se considera como lenguaje interpretado porque los programas de Pyt-hon se ejecutan por medio de un int´rprete. Existen dos maneras de usar el e
    • 1.2 ¿Qu´ es un programa? e 3int´rprete: modo de comando y modo de gui´n. En modo de comando se escri- e oben sentencias en el lenguaje Python y el int´rprete muestra el resultado. e$ pythonPython 1.5.2 (#1, Feb 1 2000, 16:32:16)Copyright 1991-1995 Stichting Mathematish Centrum, Amsterdam>>> print 1 + 12La primera l´ ınea de este ejemplo es el comando que pone en marcha el int´rprete ePython. Las dos l´ıneas siguientes son mensajes del int´rprete. La tercera l´ e ıneacomienza con >>>, que es la invitaci´n del int´rprete para indicar que est´ listo. o e aEscribimos print 1 + 1 y el int´rprete contest´ 2. e oAlternativamente, se puede escribir el programa en un archivo y usar el int´rpre- ete para ejecutar el contenido de dicho archivo. El archivo se llama, en este ca-so, un gui´n. Por ejemplo, en un editor de texto se puede crear un archivo olatoya.py que contenga esta l´ ınea:print 1 + 1Por acuerdo un´nime, los archivos que contienen programas de Python tienen anombres que terminan con .py.Para ejecutar el programa, se le tiene que indicar el nombre del gui´n al int´rpre- o ete.$ python latoya.py2En otros entornos de desarrollo los detalles de la ejecuci´n de programas pueden oser diferentes. Aem´s, la mayor´ de programas son m´s interesantes que el a ıa amencionado.La mayor´ de ejemplos en este libro se ejecutan desde en la l´ ıa ınea de comando.La l´ ınea de comando es muy apropiada para el desarrollo de programas y parapruebas r´pidas porque se pueden teclear las instrucciones de Python y se pue- aden ejecutar inmediatamente. Una vez que un programa est´ completo, puede aarchivarse en un gui´n para ejecutarlo o modificarlo en el futuro. o1.2. ¿Qu´ es un programa? eUn programa es una secuencia de instrucciones que especifican c´mo ejecutar ouna computaci´n. La computaci´n puede ser algo matem´tico, como solucionar o o a
    • 4 El Camino del Programaun sistema de ecuaciones o determinar las ra´ de un polinomio, pero tambi´n ıces epuede ser una computaci´n simb´lica, como buscar y reemplazar el texto de un o odocumento o (aunque parezca raro) compilar un programa.Las instrucciones (comandos, ´rdenes) tienen una apariencia diferente en len- oguajes de programaci´n diferentes, pero existen algunas funciones b´sicas que o ase presentan en casi todo lenguaje:entrada: Recibir datos del teclado, o un archivo u otro aparato.salida: Mostrar datos en el monitor o enviar datos a un archivo u otro aparato.matem´ticas: Ejecutar operaciones b´sicas de matem´ticas como la adici´n y a a a o la multiplicaci´n. ooperaci´n condicional: Probar la veracidad de alguna condici´n y ejecutar o o una secuencia de instrucciones apropiada.repetici´n: Ejecutar alguna acci´n repetidas veces, normalmente con alguna o o variaci´n. oLo crea o no, eso es todo. Todos los programas que existen, por complicados quesean, est´n formulados exclusivamente con tales instrucciones. As´ una manera a ı,de describir la programaci´n es: El proceso de romper una tarea en tareas cada ovez m´s peque˜as hasta que estas tareas sean suficientemente simples para ser a nejecutadas con una de estas instrucciones simples.Quiz´s esta descripci´n sea un poco ambigua. No se preocupe. Lo explicaremos a ocon m´s detalle con el tema de los algoritmos. a1.3. ¿Qu´ es la depuraci´n (debugging)? e oLa programaci´n es un proceso complejo y, por ser realizado por humanos, a omenudo desemboca en errores. Por razones caprichosas, esos errores se llamanbugs y el proceso de buscarlos y corregirlos se llama depuraci´n (en ingl´s o e“debugging”).Hay tres tipos de errores que pueden ocurrir en un programa, de sintaxis, entiempo de ejecuci´n y sem´nticos. Es muy util distinguirlos para encontrarlos o a ´mas r´pido. a
    • 1.3 ¿Qu´ es la depuraci´n (debugging)? e o 51.3.1. Errores sint´cticos aPython s´lo puede ejecutar un programa si el programa es correcto sint´ctica- o amente. En caso contrario, es decir si el programa no es correcto sint´cticamente, ael proceso falla y devuelve un mensaje de error. El t´rmino sintaxis se refiere a ela estructura de cualquier programa y a las reglas de esa estructura. Por ejem-plo, en espa˜ol la primera letra de toda oraci´n debe ser may´scula, y todas las n o uoraciones deben terminar con un punto. esta oraci´n tiene un error sint´ctico. o aEsta oraci´n tambi´n o ePara la mayor´ de lectores, unos pocos errores sint´cticos no son significatvos, ıa ay por eso pueden leer la poes´ de e. e. cummings sin anunciar errores de sin- ıataxis. Python no es tan permisivo. Si hay aunque sea un solo error sint´ctico aen el programa, Python mostrar´ un mensaje de error y abortar´ la ejecuci´n a a odel programa. Durante las primeras semanas de su carrera como programadorpasar´, seguramente, mucho tiempo buscando errores sint´cticos. Sin embargo, a atal como adquiera experiencia tendr´ menos errores y los encontrar´ mas r´pido. a a a1.3.2. Errores en tiempo de ejecuci´n oEl segundo tipo de error es un error en tiempo de ejecuci´n. Este error no aparece ohasta que se ejecuta el programa. Estos errores tambi´n se llaman excepciones eporque indican que algo excepcional (y malo) ha ocurrido.Con los programas que vamos a escribir al principio, los errores en tiempo deejecuci´n ocurrir´n con poca frecuencia, as´ que puede pasar bastante tiempo o a ıhasta que vea uno.1.3.3. Errores sem´nticos aEl tercer tipo de error es el error sem´ntico. Si hay un error de l´gica en su a oprograma, el programa se ejecutar´ sin ning´n mensaje de error, pero el resul- a utado no ser´ el deseado. Ser´ cualquier otra cosa. Concretamente, el programa a ahar´ lo que usted le dijo. aA veces ocurre que el programa escrito no es el programa que se ten´ en mente. ıaEl sentido o significado del programa (su sem´ntica) no es correcto. Es dif´ ha- a ıcilllar errores de l´gica, porque requiere trabajar al rev´s, observando el resultado o edel programa para averiguar lo que hace.
    • 6 El Camino del Programa1.3.4. Depuraci´n experimental oUna de las t´cnicas m´s importantes que usted aprender´ es la depuraci´n. Aun- e a a oque a veces es frustrante, la depuraci´n es una de las partes m´s intelectualmente o aricas, interesantes y estimulantes de la programaci´n. oLa depuraci´n es una actividad parecida a la tarea de un investigador: se tie- onen que estudiar las claves para inducir los procesos y eventos llevaron a losresultados que tiene a la vista.La depuraci´n tambi´n es una ciencia experimental. Una vez que se tiene la o eidea de cu´l es el error, se modifica el programa y se intenta nuevamente. Si su ahip´tesis fue la correcta se pueden predecir los resultados de la modificaci´n y o oestar´ m´s cerca de un programa correcto. Si su hip´tesis fue err´nea tendr´ que a a o o aidearse otra hip´tesis. Como dijo Sherlock Holmes, “Cuando se ha descartado olo imposible, lo que queda, no importa cuan inveros´ ımil, debe ser la verdad.”(A. Conan Doyle, The Sign of Four)Para algunas personas, la programaci´n y la depuraci´n son lo mismo: la pro- o ogramaci´n es el proceso de depurar un programa gradualmente hasta que haga olo que usted quiera. La idea es que deber´ usted comenzar con un programa ıaque haga algo y hacer peque˜as modificaciones, depur´ndolas sobre la marcha, n ade modo que siempre tenga un programa que funcione.Por ejemplo, Linux es un sistema operativo que contiee miles de l´ ıneas de c´digo, opero Linus Torvalds lo comenz´ como un programa para explorar el microproce- osador Intel 80836. Seg´n Larry Greenfield, “Uno de los proyectos tempranos de uLinus fue un programa que alternaba la impresi´n de AAAA con BBBB. Este oprograma evolucion´ en Linux” (de The Linux Users’Guide Versi´n Beta 1). o oOtros cap´ ıtulos tratar´n m´s acerca del tema de depuraci´n y otras t´cnicas de a a o eprogramaci´n. o1.4. Lenguajes formales y lenguajes naturalesLos lenguajes naturales son los lenguajes hablados por seres humanos, comoel espa˜ol, el ingl´s y el franc´s. No los han dise˜ados personas (aunque se n e e nintente poner cierto orden en ellos), sino que se han desarrollado naturalmente.Los lenguajes formales son lenguajes dise˜ados por humanos y que tienen naplicaciones espec´ıficas. La notaci´n matem´tica, por ejemplo, es un lenguaje o aformal ya que se presta a la representaci´n de las relaciones entre n´meros y o us´ ımbolos. Los qu´ımicos utilizan un lenguaje formal para representar la estructuraqu´ımica de las mol´culas. Y lo m´s importante: e a
    • 1.4 Lenguajes formales y lenguajes naturales 7 Los lenguajes de programaci´n son lenguajes formales de- o sarrollados para expresar computaciones.Los lenguajes formales casi siempre tienen reglas sint´cticas estrictas. Por ejem- aplo, 3 + 3 = 6 es una expresi´n matem´tica correcta, pero 3 = +6$ no lo es. De o ala misma manera, H2 0 es una nomenclatura qu´ ımica correcta, pero 2 Zz no loes.Existen dos clases de reglas sint´cticas, en cuanto a unidades y estructura. Las aunidades son los elementos b´sicos de un lenguaje, como lo son las palabras, los an´meros y los elementos qu´ u ımicos. Por ejemplo, en 3=+6$, $ no es una unidadmatem´tica aceptada (al menos hasta donde nosotros sabemos. Similarmente, a2 Zz no es formal porque no hay ning´ n elemento con la abreviatura Zz. uLa segunda clase de regla sint´ctica est´ relacionada con la estructura de un a aelemento; o sea, el orden de las unidades. La estructura de la sentencia 3=+6$no se acepta porque no se puede escribir el s´ ımbolo de igualdad seguido de un ımbolo positivo. Similarmente, las f´rmulas moleculares tienen que mostrar els´ on´mero de sub´ u ındice despu´s del elemento, no antes. e A manera de pr´ctica, trate de producir una oraci´n con estructura a o aceptada pero que est´ compuesta de unidades irreconocibles. Luego e escriba otra oraci´n con unidades aceptables pero con estructura no o v´lida. aAl leer una oraci´n, sea en un lenguaje natural o una sentencia en un lenguaje ot´cnico, se debe discernir la estructura de la oraci´n. En un lenguaje natural e oeste proceso, llamado an´lisis sint´ctico ocurre subconscientemente. a aPor ejemplo cuando usted escucha la oraci´n “El otro zapato cay´”, entiende o oque “el otro zapato” es el sujeto y “cay´” es el verbo. Cuando se ha analizado ola oraci´n sint´cticamente, se puede deducir el significado, o la sem´ntica, de la o a aoraci´n. Suponiendo que sepa lo ques es un zapato y lo que es caer, entender´ el o asignificado de la oraci´n. oAunque existen muchas cosas en com´n entre los lenguajes naturales y los ulenguajes formales—por ejemplo las unidades, la estructura, la sintaxis y lasem´ntica—tambi´n existen muchas diferencias: a eambig¨ edad: Los lenguajes naturales tienen much´ u ısimas ambig¨edades, que u los hablantes sortean usando claves contextuales y otra informaci´n. Los o lenguajes formales se dise˜an para estar completamente libres de am- n big¨edades, o tanto como sea posible, lo que quiere decir que cualquier u sentencia tiene s´lo un significado, sin importar el contexto. o
    • 8 El Camino del Programaredundancia: Para reducir la ambig¨edad y los malentendidos, las lenguas na- u turales utilizan bastante redundancia. Como resultado suelen ser prolijos. Los lenguajes formales son menos redundantes y m´s concisos. aliteralidad: Los lenguajes naturales tienen muchas met´foras y frases hechas. a El significado de un dicho, por ejemplo “Estirar la pata”, es diferente al significado de sus sustantivos y verbos. En este ejemplo, la oraci´n no tiene o nada que ver con un pie y significa ’morirse’. Los lenguajes formales no difieren de su significado literal.Los que aprenden a hablar un lenguaje natural—es decir, todo el mundo—muchas veces tienen dificultad en adaptarse a los lenguajes formales. A vecesla diferencia entre los lenguajes formales y los naturales es comparable a ladiferencia entre la prosa y la poes´ ıa:Poes´ Se utiliza una palabra por su cualidad auditiva tanto como por su signi- ıa: ficado. El poema, en su totalidad, produce un efecto o reacci´n emocional. o La ambig¨edad no es solo com´n sino utilizada a prop´sito. u u oProsa: El significado literal de la palabra es mas importante y la estructura da m´s significado a´n. La prosa se presta al an´lisis m´s que la poes´ pero a u a a ıa, todav´ contiene ambig¨edad. ıa uProgramas: El significado de un programa es inequ´ ıvoco y literal, y es enten- dido en su totalidad analizando las unidades y la estructura.He aqu´ unas sugerencias para la lectura de un programa (y de otros lenguajes ıformales). Primero, recuerde que los lenguajes formales son mucho m´s densos aque los lenguajes naturales, y por consecuente lleva m´s tiempo leerlos. Tam- abi´n, la estructura es muy importante, as´ que entonces no es una buena idea e ıleerlo de pies a cabeza, de izquierda a derecha. En vez de eso, aprenda a sepa-rar las diferentes partes en su mente, identificar las unidades e interpretar laestructura. Finalmente, ponga atenci´n a los detalles. Los fallos de puntuaci´n o oy la ortograf´ que puede obviar en el lenguaje natural, pueden suponer una ıa,gran diferencia en un lenguaje formal.1.5. El primer programaTradicionalmente el primer programa en un lenguaje nuevo se llama “Hola,mundo” (Hello world!) porque s´lo muestra las palabras “Hola a todo el mundo”. oEn Python es as´ ı:print "Hola, mundo"
    • 1.6 Glosario 9Este es un ejemplo de una sentencia print, la cual no imprime nada en papel,m´s bien muestra un valor. En este caso, el resultado es las palabras aHola, mundoLas comillas se˜alan el comienzo y el final del valor; no aparecen en el resultado. nAlguna gente eval´a la calidad de un lenguaje de programaci´n por la simplici- u odad del programa “Hola, mundo”. Si seguimos ese criterio, Python cumple contodas sus metas.1.6. Glosariosoluci´n de problemas: El proceso de formular un problema, hallar la solu- o ci´n y expresar esa soluci´n. o olenguaje de alto nivel: Un lenguaje como Python dise˜ado para ser f´cil de n a leer y escribir para la gente.lenguaje de bajo nivel: Un lenguaje de programaci´n dise˜ado para ser f´cil o n a de ejecutar para un computador; tambi´n se lo llama “lenguaje de m´qui- e a na” o “lenguaje ensamblador”.portabilidad: La cualidad de un programa que le permite ser ejecutado en m´s de un tipo de computador. ainterpretar: Ejecutar un programa escrito en un lenguaje de alto nivel tradu- ci´ndolo l´ e ınea por l´ ıneacompilar: Traducir un programa escrito en un lenguaje de alto nivel a un len- guaje de bajo nivel todo al mismo tiempo, en preparaci´n para la ejecuci´n o o posterior.c´digo fuente: Un programa escrito en un lenguaje de alto nivel antes de ser o compilado.c´digo de objeto: La salida del compilador una vez que ha traducido el pro- o grama.programa ejecutable: Otro nombre para el c´digo de objeto que est´ listo o a para ejecutarse.gui´n: Un programa archivado (que va a ser interpretado). oprograma: Un conjunto de instrucciones que especifica una computaci´n. oalgoritmo: Un proceso general para resolver una clase completa de problemas.
    • 10 El Camino del Programaerror (bug): Un error en un programa.depuraci´n: El proceso de hallazgo y eliminaci´n de los tres tipos de errores o o de programaci´n. osintaxis: La estructura de un programa.error sint´ctico: Un error en un programa que hace que el programa sea im- a posible de analizar sint´cticamente (e imposible de interpretar). aerror en tiempo de ejecuci´n: Un error que no ocurre hasta que el progra- o ma ha comenzado a ejecutarse e impide que el programa contin´e. uexcepci´n: Otro nombre para un error en tiempo de ejecuci´n. o oerror sem´ntico: Un error en un programa que hace que ejecute algo que no a era lo deseado.sem´ntica: El significado de un programa. alanguage natural: Cualquier lenguaje hablado que evolucion´ de forma natu- o ral.lenguaje formal: Cualquier lenguaje dise˜ado por humanos que tiene un n prop´sito espec´ o ıfico, como la representaci´n de ideas matem´ticas o pro- o a gramas de computadores; todos los lenguajes de programaci´n son lengua- o jes formales.unidad: Uno de los elementos b´sicos de la estructura sint´ctica de un progra- a a ma, an´logo a una palabra en un lenguaje natural. aan´lisis sint´ctico: La examinaci´n de un programa y el an´lisis de su estruc- a a o a tura sint´ctica. asentencia print: Una instrucci´n que causa que el int´rprete Python muestre o e un valor en el monitor.
    • Cap´ ıtulo 2Variables, expresiones ysentencias2.1. Valores y tiposEl valor es uno de los elementos fundamentales (como por ejemplo una letra oun n´mero) que manipula un programa. Los valores que hemos visto hasta el umomento son 2 (el resultado de sumar 1 + 1) y Hola, mundo.Estos valores son de distintos tipos: 2 es un entero y Hola, mundo es unacadena, llamada as´ porque contiene una “cadena” de letras. Usted (y el ıint´rprete) puede identificar las cadenas porque est´n encerradas entre comi- e allas.La sentencia print tambi´n funciona con enteros: e>>> print 44Si no est´ seguro del tipo que tiene un determinado valor, puede pregunt´rselo a aal int´rprete de Python. e>>> type("Hola, mundo")<type ’string’>>>> type(17)<type ’int’>No es sorprendente que las cadenas sean de tipo string (cadena en ingl´s) y elos enteros sean de tipo int (por integer en ingl´s). De forma menos obvia, los e
    • 12 Variables, expresiones y sentenciasn´meros con decimales (separados por medio de un punto en ingl´s) son de tipo u efloat debido a la representaci´n de estos n´meros en el formato llamado de o ucoma flotante (floating-point).>>> type(3.2)<type ’float’>¿Qu´ ocurre con los valores como "17" y "3.2"? Parecen n´meros, pero est´n e u aentre comillas como las cadenas.>>> type("17")<type ’string’>>>> type("3.2")<type ’string’>Son cadenas.Cuando escriba un entero largo, podr´ estar tentado de usar comas entre grupos ıade tres d´ ´ ıgitos, como en 1,000,000. Este no es un entero legal en Python, peroes una expresi´n legal: o>>> print 1,000,0001 0 0En fin, no era eso lo que quer´ ıamos. Python interpreta 1,000,000 como unalista de tres n´meros que debe imprimir. As´ que recuerde no insertar comas en u ısus enteros. 12.2. VariablesUna de las caracter´ ısticas m´s potentes de los lenguajes de programaci´n es a ola capacidad de manipular variables. Una variable es un nombre que hacereferencia a un valor.La sentencia de asignaci´n crea nuevas variables y les asigna un valor: o>>> mensaje = "Que onda?">>> n = 17>>> pi = 3.14159Este ejemplo muestra tres asignaciones. La primera de ellas asigna el valor"Que onda?" a una variable nueva, de nombre mensaje. La segunda le da el 1 El uso de la coma y el punto en n´mero es en ingl´s el contrario al uso espa˜ol, como se u e napunt´ en una nota anterior o
    • 2.3 Nombres de variables y palabras reservadas 13valor entero 17 a n, y la tercera le da el valor de n´mero en coma flotante u3.14159 a pi.Una forma habitual de representar variables sobre el papel es escribir el nombrecon una flecha se˜alando al valor de la variable. Este tipo de representaci´n n ose llama diagrama de estado, ya que muestra en qu´ estado se halla cada euna de las variables (consid´relo como el “estado de ´nimo” de la variable”). e aEl siguiente diagrama muestra el efecto de las tres sentencias de asignaci´n oanteriores: mensaje "Que onda?" n 17 pi 3.14159La sentencia print tambi´n funciona con variables. e>>> print mensaje"Que onda?">>> print n17>>> print pi3.14159En cada caso, el resultado es el valor de la variable. Las variables tambi´n tienen etipo. De nuevo, podemos preguntar al int´rprete lo que son. e>>> type(mensaje)<type ’string’>>>> type(n)<type ’int’>>>> type(pi)<type ’float’>El tipo de una variable es el tipo del valor al que se refiere.2.3. Nombres de variables y palabras reservadasComo norma general, los programadores eligen nombres significativos para susvariables: esto permite documentar para qu´ se usa la variable. eLos nombres de las variables pueden tener una longitud arbitraria. Pueden estarformados por letras y n´meros, pero deben comenzar con una letra. Aunque es u
    • 14 Variables, expresiones y sentenciasaceptable usar may´sculas, por convenci´n no lo hacemos. Si lo hace, recuerde u oque la distinci´n es importante: Bruno y bruno son dos variables diferentes. oEl gui´n bajo ( ) tambi´n es legal y se utiliza a menudo para separar nombres o econ m´ltiples palabras, como mi nombre o precio del cafe colombiano. uSi intenta darle a una variable un nombre ilegal, obtendr´ un error de sintaxis. a>>> 76trombones = "gran desfile"SyntaxError: invalid syntax>>> mas$ = 1000000SyntaxError: invalid syntax>>> class = "Curso de Programaci´n 101" oSyntaxError: invalid syntax76trombones es ilegal porque no comienza por una letra. mas$ es ilegal porquecontiene un car´cter ilegal, el signo del d´lar. Pero ¿qu´ tiene de malo class? a o eResulta que class es una de las palabras reservadas de Python. El lenguajeusa las palabras reservadas para definir sus reglas y estructura, y no puedenusarse como nombres de variables.Python tiene 28 palabras reservadas: and continue else for import not raise assert def except from in or return break del exec global is pass try class elif finally if lambda print whileTal vez quiera mantener esta lista a mano. Si el int´rprete se queja de alguno ede sus nombres de variable, y usted no sabe por qu´, compruebe si est´ en esta e alista.2.4. SentenciasUna sentencia es una instrucci´n que puede ejecutar el int´rprete de Python. o eHemos visto dos tipos de sentencias: print y la asignaci´n. oCuando usted escribe una sentencia en la l´ ınea de comandos, Python la ejecutay muestra el resultado, si lo hay. El resultado de una sentencia print es un valor.Las sentencias de asignaci´n no entregan ning´n resultado. o uNormalmente un gui´n contiene una secuencia de sentencias. Si hay m´s de una o asentencia, los resultados aparecen de uno en uno tal como se van ejecutando lassentencias.
    • 2.5 Evaluar expresiones 15Por ejemplo, el gui´n oprint 1x = 2print xprsenta la salida12De nuevo, la sentencia de asignaci´n no produce ninguna salida. o2.5. Evaluar expresionesUna expresi´n es una combinaci´n de valroes, variables y operadores. Si teclea o ouna expresi´n en la l´ o ınea de comandos, el int´rprete la eval´ a y muestra el e uresultado:>>> 1 + 12Un valor, y tambi´n una variable, se considera una expresi´n por s´ mismo. e o ı>>> 1717>>> x2Para complicar las cosas, evaluar una expresi´n no es del todo lo mismo que oimprimir un valor.>>> mensaje = "Que onda?">>> mensaje"Que onda?">>> print mensajeQue onda?Cuando Python muestra el valor de una expresi´n, usa el mismo formato que ousted usar´ para introducir un valor. En el caso de las cadenas, eso significa que ıaincluye las comillas. Pero la sentencia print imprime el valor de la expresi´n, lo oque en este caso es el contenido de la cadena.En un gui´n, una expresi´n sin m´s es una sentencia v´lida, pero no hace nada. o o a aEl gui´n o
    • 16 Variables, expresiones y sentencias173.2"Hola, mundo"1 + 1no presenta ninguna salida. ¿C´mo cambiar´ usted el gui´n para mostrar los o ıa ovalores de estas cuatro expresiones?2.6. Operadores y expresionesLos operadores son s´ ımbolos especiales que representan c´lculos simples, como ala suma y la multiplicaci´n. Los valores que usa el operador se llaman operan- odos.Las siguientes expresione son legales en Python y su significado es m´s o menos aclaro:20+32 hora-1 hora*60+minuto minuto/60 5**2 (5+9)*(15-7)Los s´ ımbolos +, -, /, y el uso de los par´ntesis para el agrupamiento, se usan etodos de la misma forma que en matem´ticas. El asterisco (*) es el signo de amultiplicaci´n y ** el s´ o ımbolo para exponenciaci´n. oCuando aparece el nombre de una variable en el lugar de un operando, se sus-tituye con su valor antes de realizar la operaci´n. oLa suma, resta, multiplicaci´n y exponenciaci´n hacen lo esperado, pero la divi- o osi´n le puede sorprender. La operaci´n que sigue tiene un resultado inesperado: o o>>> minuto = 59>>> minuto/600El valor de la variable minuto es 59, y 59 dividido entre 60 es 0.98333 y no0. El motivo de la discrepancia reside en que Python est´ llevando a cabo una adivisi´n de enteros. oCuando ambos operandos son enteros, el resultado ha de ser tambi´n un entero; epor convenci´n, la divisi´n de enterios simpre se redondea a la baja, incluso en o ocasos como estos en los que el siguiente entero est´ muy pr´ximo. a oUna alternativa posible en este caso es el c´lculo de un porcentaje y no el de auna fracci´n: o>>> minuto*100/6098
    • 2.7 El orden de las operaciones 17De nuevo se redondea el resultado a la baja, pero al menos ahora la respuestaes aproximadamente correcta. Otra alternativa es la divisi´n de coma flotante, oque veremos en el Cap´ ıtulo 3.2.7. El orden de las operacionesCuando aparece m´s de un operador en una expresi´n, el orden de evaluaci´n a o odepende de las reglas de precedencia. Python sigue las mismas reglas deprecedencia que los propios matem´ticos para sus operaciones matem´ticas. a aLos ingleses usan el acr´nimo PEMDAS como regla parea recordar el orden de olas operaciones: Par´ntesis: tienen la precedencia m´s alta y pueden usarse para forzar que e a una expresi´n se eval´e en el orden que queramos nosotros. Puesto que las o u expresiones entre par´ntesis se eval´an primero, 2 * (3-1) es igual a 4, y e u (1+1)**(5-2) es igual a 8. Tambi´n puede usar par´ntesis para que una e e expresi´n sea m´s legible; por ejemplo (minuto * 100) / 60, aunque el o a resultado no cambie de todas formas. Exponenciaci´n tiene la siguiente precedencia m´s alta; as´ pues 2**1+1 o a ı es igual a 3 y no a 4, y 3*1**3 es igual a 3 y no a 27. La Multiplicaci´n y la Divisi´n tienen la misma precedencia, que es m´s o o a alta que la de la Adici´n y la Sustracci´n, que tienen tambi´n la misma o o e precedencia. Por tanto 2*3-1 devuelve 5 y no 4, y 2/3-1 da -1, y no 1 (recuerde que en la divisi´n de enteros 2/3 da 0). o Los operadores que tienen la misma precedencia se eval´an de izquierda u a derecha. As´ en la expresi´n minuto*100/60, tiene lugar primero la ı, o multiplicaci´n, devolviendo tt 5900/60, que a su vez da como resultado o 98. Si las operaciones se hubiesen realizado de derecha a izquierda, el resultado habr´ sido 59/1 que da 59, y que es incorrecto. ıa2.8. Las operaciones sobre cadenasEn general no es posible realizar operaciones matem´ticas con cadenas, incluso si alas cadenas parecen n´meros. Las siguientes sentencias son ilegales (suponiendo uque mensaje sea de tipo string) mensaje-1 "Hola"/123 mensaje*"Hola" "15"+2
    • 18 Variables, expresiones y sentenciasEs curioso que el operador + funcione con cadenas, aunque no haga exactamentelo que usted esperar´ Para cadenas, el operador + representa la concatena- ıa.ci´n, lo que significa que se unen los dos operandos uni´ndolos extremo con o eextremo. Por ejemplo:fruta = "pl´tano" abizcochoBueno = " pan de leche"print fruta + bizcochoBuenoLa salida del programa es pl´tano pan de leche. El espacio delante de pan aes parte de la cadena, y es necesario para introducir el espacio que separa lascadenas concatenadas.El operador * tambi´n funciona con cadenas; lleva a cabo la repetici´n. Por e oejemplo ’Chiste’*3 es ’ChisteChisteChiste’. Uno de los operandos ha deser una cadena, el otro ha de ser un entero.Por un lado, esta interpretaci´n de + y * cobra sentido por analog´ con la o ıasuma y la multimplicaci´n. Igual que 4*3 es equivalente a 4+4+4, esperamos oque ’Chiste’*3 sea lo mismo que ’Chiste’+’Chiste’+’Chiste’, y as´ es. Porıotro lado, la concatenaci´n y la repetici´n son de alguna manera muy diferentes o ode la adici´n y la multiplicaci´n de enteros. ¿Puede encontrar una propiedad que o otienen la suma y la multiplicaci´n de enteros y que no tengan la concatenaci´n o oy la repetici´n de cadenas? o2.9. Composici´n oHasta ahora hemos examinado los elementos de un programa (variables, expre-siones y sentencias) por separado, sin hablar de c´mo combinarlos. oUna de las caracter´ısticas m´s utiles de los lenguajes de programaci´n es su a ´ ocapacidad de tomar peque˜os bloques de construcci´n y ensamblarlos. Por n oejemplo, sabemos c´mo sumar n´meros y c´mo imprimirlos; resulta que pode- o u omos hacer las dos cosas a un tiempo:>>> print 17 + 320En realidad, no debemos decir “al mismo tiempo”, puesto que en realidad lasuma tiene que realizarse antes de la impresi´n, pero la cuesti´n es que cualquier o oexpresi´n relacionada con n´meros, cadenas y variables puede usarse dentro de o uuna sentencia print. Ya hemos visto un ejemplo de ello:print "N´mero de minutos desde la medianoche: ", hora*60+minuto u
    • 2.10 Los comentarios 19Y tambi´n puede poner expresiones arbitrarias en la parte derecha de una sen- etencia de asignaci´n: oporcentaje = (minuto * 100) / 60Esta capacidad puede no resultar muy sorprendente, pero ya veremos otrosejemplos donde la composici´n hace posible expresar c´lculos complejos con o alimpieza y brevedad. ´ATENCION: Hay l´ ımites al lugar donde pueden usarse ciertas expresiones. Porejemplo, la parte izquierda de una sentencia de asignaci´n tiene que ser un onombre de variable, no una exrpresi´n. Por tanto es ilegal lo siguiente: minute+1 o= hour.2.10. Los comentariosConforme los programas van creciendo de tama˜o y complic´ndose, se vuelven n am´s complicados de leer. Los lenguajes formales son densos y con frecuencia es adif´ observar un trozo de c´digo y averiguar lo que hace, o por qu´ lo hace. ıcil o ePor ello es una buena idea a˜adir notas a su programa que expliquen, en un nlenguaje natural, qu´ hace el programa. Estas notas se llaman comentarios y ese marcan con el s´ ımbolo #:# calcula el porcentaje de la hora que ha pasado yaporcentaje = (minuto * 100) / 60En este caso, el comentario aparece en una l´ ınea propia. Tambi´n puede poner ecomentarios al final de otras l´ ıneas:porcentaje = (minuto * 100) / 60 # ojo: divisi´n de enteros oTodo lo que va del # al final de la l´ınea se ignora (no tiene efecto sobre elprograma). El mensaje est´ destinado al programador, o a futuros programa- adores que podr´ tener que usar el c´digo. En este caso avisa al lector sobre el ıan osorprendente comportamiento de la divisi´n de enteros. o2.11. Glosariovalor: un n´mero o cadena (o cualquier otra cosa que se especifique poste- u riormente) que puede almacenarse en una variable o calcularse en una expresi´n. o
    • 20 Variables, expresiones y sentenciastipo: un conjunto de valores. El tipo de un valor determina c´mo puede usarse o en las expresiones. Hasta ahora, los tipos que hemos visto son enteros (tipo int), n´meros de coma flotante (tipo float) y cadenas (tipo string). ucoma flotante: un formato para representar n´meros con decimales. uvariable: nombre que hace referencia a un valor.sentencia: es una porci´n de c´digo que representa una orden o acci´n. Hasta o o o ahora, las sentencias que hemos vistos son las asignaciones y las sentencias print.asignaci´n: sentencia que asigna un valor a una variable. odiagrama de estado: representaci´n gr´fica de un conjunto de variables y de o a los valores a los que se refiere.palabra reservada: es una palabra clave que usa el compilador para analizar sint´cticamente los programas. No pueden usarse palabras reservadas, por a ejemplo if, def y while como nombres de variables.operador: un s´ ımbolo especial que representa un c´lculo sencillo, como la su- a ma, la multiplicaci´n o la concatenaci´n de cadenas. o ooperando: uno de los valores sobre los que act´a un operador. uexpresi´n: una combinaci´n de variables, operadores y valores. Dicha combi- o o naci´n representa un unico valor como resultado. o ´evaluar: simplificar una expresi´n ejecutando las operaciones para entregar un o valor unico. ´divisi´n de enteros: es una operaci´n que divide un entero entre otro y de- o o vuelve un entero. La divisi´n de enteros devuelve s´lo el n´mero entero o o u de veces que el numerador es divisible por en denominador, y descarta el resto.reglas de precedencia: la serie de reglas que especifican el orden en el que las expresiones con m´tiples operadores han de evaluarse. uconcatenar: unir dos operandos extremo con extremo.composici´n: la capacidad de combinar expresiones sencillas y sentencias has- o ta crear sentencias y expresiones compuestas, con el fin de representar c´lculos complejos de forma concisa. acomentario: un segmento de informaci´n en un programa, destinado a otros o programadores (o cualquiera que lea el c´digo fuente) y que no tiene efecto o sobre la ejecuci´n del programa. o
    • Cap´ ıtulo 3Funciones3.1. Llamadas a funcionesYa hemos visto un ejemplo de una llamada a una funci´n: o >>> type("32") <type ’string’>El nombre de la funci´n es type, y muestra el tipo de un valor o de una variable. oEl valor o variable, llamado el argumento de la funci´n, ha de estar encerrado oentre par´ntesis. Es habitual decir que una funci´n “toma” un argumento y e o“devuelve” un resultado. El resultado se llama valor de retorno.En lugar de imprimir el valor de retorno, podemos asign´rselo a una variable. a >>> nereida = type("32") >>> print nereida <type ’string’>Otro ejemplo m´s: la funci´n id toma como argumento un valor o una variable a oy devuelve un entero que act´a como identificador unico de ese valor. u ´ >>> id(3) 134882108 >>> yanira = 3 >>> id(yanira) 134882108Cada valor tiene un id, que es un valor unico relacionado con d´nde se almacena ´ oen la memoria del computador. El id de una variable es el id del valor al quehace referencia.
    • 22 Funciones3.2. Conversi´n de tipos oPython proporciona una colecci´n de funciones internas que convierten valores ode un tipo a otro. La funci´n int toma un valor y lo convierte a un entero, si oes posible, o da un error si no es posible. >>> int("32") 32 >>> int("Hola") ValueError: invalid literal for int(): Holaint tambi´n convierte valores de coma flotante a enteros, pero recuerde que esiempre redondea hacia abajo: >>> int(3.99999) 3La funci´n float que convierte enteros y cadenas en n´meros en coma flotante: o u >>> float(32) 32.0 >>> float("3.14159") 3.14159Finalmente, est´ la funci´n str, que convierte a tipo string: a o >>> str(32) ’32’ >>> str(3.14149) ’3.14149’Pudiera parecer extra˜o que Python distinga entre el valor entero 1 y el valor nde coma flotante 1.0. Tal vez representen el mismo n´mero, pero pertenecen ua tipos distintos. El motivo es que se representan de forma distinta dentro delcomputador.3.3. Coerci´n de tipos oAhora que ya sabemos convertir entre tipos, tenemos otra forma de enfrentarnosa la divisi´n de enteros. Volviendo al ejemplo del cap´ o ıtulo anterior, suponga quequeremos calcular qu´ fracci´n de una hora hab´ transcurrido. La expresi´n e o ıa om´s obvia, minuto / 60, realiza una divisi´n de enteros, por lo que el resultado a oes siempre 0, incluso 59 minutos despu´s de la hora. eUna alternativa es convetir minuto a tipo float (coma flotante) y luego efectuaruna divisi´n de coma flotante: o
    • 3.4 Funciones matem´ticas a 23 >>> minuto = 59 >>> float(minuto) / 60.0 0.983333333333O bien podemos sacar provecho de las reglas de la conversi´n autom´tica de o atipos, llamada coerci´n de tipos. Para los operadores matem´ticos, si uno de o alos operandos matem´ticos es tipo float, el otro se convierte autom´ticamente a aen float. >>> minuto = 59 >>> minuto / 60.0 0.983333333333Al usar un denomidador que es float, obligamos a Python a hacer divisi´n de ocoma flotante.3.4. Funciones matem´ticas aEs posible que ya haya visto usted en matem´ticas funciones como sin (seno) y alog, y que haya aprendido a evaluar expresiones como sin(pi/2) y log(1/x).Primero eval´a la expresi´n entre par´ntesis, (el argumento). Por ejemplo, pi/2 u o ees aproximadamente 1.571, y 1/x es 0.1 (si x es igual a 10.0).Luego eval´a la funci´n en s´ misma, bien mir´ndola en una tabla, bien llevando u o ı aa cabo diversos c´lculos. El sin (seno) de 1.571 es 1, y el log de 0.1 es -1 a(suponiendo que log indique el logaritmo de base 10).Este proceso puede aplicarse repetidamente para evaluar expresiones m´s com- aplicadas como log(1/sin(pi/2)). Primero evaluamos el argumento de la fun-ci´n m´s interna, luego se eval´a la funci´n, y as´ sucesivamente. o a u o ıPython dispone de un m´dulo matem´tico que proporciona la mayor´ de las o a ıafunciones matem´ticas habituales. Un m´dulo es un archivo que contiene una a ocolecci´n de funciones agrupadas juntas. oAntes de poder usar las funciones de un m´dulo, tenemos que importarlo: o >>>import mathPara llamar a una de las funciones, tenemos que especificar el nombre del m´dulo oy el nombre de la funci´n, separados por un punto. A esto se le llama notaci´n o ode punto: decibelio = math.log10 (17.0) angulo = 1.5 altura = math.sin(angulo)
    • 24 FuncionesLa primera sentencia da a decibelio el valor del logaritmo de 17, en base 10.Hay tambi´n una funci´n llamada log que toma logaritmos en base e. e oLa tercera sentencia halla el seno del valor de la variable angulo. sin y las otrasfunciones trigonom´tricas (cos, tan, etc.) toman sus argumentos en radianes. ePara convertir de grados a radianes, puede dividir por 360 y multiplicar por2*pi. Por ejemplo, para hallar el seno de 45 grados, calcule primero el ´ngulo aen radianes y luego halle el seno: grados = 45 angulo = grados * 2 * math.pi / 360.0 math.sin(angulo)La constante pi tambi´n es parte del m´dulo math. Si se sabe la geometr´ puede e o ıa,verificar el resultado compar´ndolo con el de la ra´ cuadrada de 2, dividida entre a ız2. >>> math.sqrt(2) / 2.0 0.7071067811873.5. Composici´n oIgual que con las funciones matem´ticas, las funciones de Python se pueden acomponer; eso quiere decir que se usa una expresi´n como parte de otra. Por oejemplo, puede usar cualquier expresi´n como argumento de una funci´n: o o x = math.cos(angulo + pi/2)Esta sentencia toma el valor de pi, lo divide entre dos y le a˜ade el resultado nal valor de angulo. La suma se pasa luego como argumento a la funci´n cos. oTambi´n puede tomar el resultado de una funci´n y pas´rselo como argumento e o aa otra: x = math.exp(math.log(10.0))Esta sentencia encuentra el logaritmo en base e de 10 y luego eleva e a eseexponente. El resultado queda asignado a x.3.6. A˜ adir funciones nuevas nHasta ahora s´lo hemos usado las funciones que vienen incluidas con Python, opero tambi´n es posible a˜adir nuevas funciones. La creaci´n de nuevas funciones e n opara resolver sus problemas partigulares es una de las cosas m´s utiles de los a ´lenguajes de programaci´n de prop´sito general. o o
    • 3.6 A˜ adir funciones nuevas n 25En contextos de programaci´n, funci´n es una secuencia de instrucciones con o onombre, que lleva a cabo la operaci´n deseada. Esta operaci´n se especifica en o ouna definici´n de funci´n. Las funciones que hemos usado hsta ahora las han o odefinido por nosotros, y esas definiciones est´n ocultas. Eso es bueno, ya que nos apermite usar funciones sin preocuparnos sobre los detalles de sus definiciones.La sintaxis de la definici´n de una funci´n es: o o def NOMBRE( LISTA DE PARAMETROS ): SENTENCIASPuede inventarse el nombre que desee para su funci´n, con la excepci´n de o oque no puede usar las palabras reservadas de Python. La lista de par´metros aespecifica qu´ informaci´n, en caso de haberla, ha de proporcionar para usar la e ofunci´n nueva. oPuede haber cualquier n´mero de sentencias dentro de la funci´n, pero tienen u oque estar indentadas desde el margen izquierdo. En los ejemplos de este libro seusar´ una indentaci´n de dos espacios. a oEl primer par de funciones que escribiremos no tienen par´metros, de manera aque su sintaxis es: def nueva_linea(): printEsta funci´n se llama nueva linea. Los par´ntesis vac´ indican que no tiene o e ıospar´metros. Contiene una unica sentencia, que muestra como salida un car´cter a ´ ade nueva l´ ınea (es lo que sucede cuando utiliza una orden print sin argumentos).Llamamos entonces a la funci´n nueva usando la misma sintaxis que usamos opara las funciones internas: print "Primera linea." nueva_linea() print "Segunda linea."The output of this program is Primera linea. Segunda linea.Observe el espacio a˜adido que hay entre las dos l´ n ıneas. Si quisi´ramos m´s e aespacios, entre las l´ ıneas, ¿qu´ har´ e ıamos? Podemos llamar varias veces a lamisma funci´n: o print "Primera linea." nueva_linea() nueva_linea() nueva_linea() print "Segunda linea."
    • 26 FuncionesO bien podemos escribir una nueva funci´n que llamaremos tresLineas, y que oimprima tres nuevas l´ ıneas: def tresLineas(): nueva_linea() nueva_linea() nueva_linea() print "Primera Linea." tresLineas() print "Segunda Linea."Esta funci´n contiene tres sentencias, las cuales est´n todas indentadas con dos o aespacios. Puesto que la siguiente sentencia no est´ indentada, Python sabe que ano es parte de la funci´n. oObserve los siguientes puntos con respecto a este programa: 1. Se puede llamar al mismo procedimiento repetidamente. De hecho es bas- tante util hacerlo, adem´s de habitual. ´ a 2. Se puede llamar a una funci´n desde dentro de otra funci´n: en este caso o o tresLineas llama a nueva linea.Hasta ahora puede no haber quedar claro por qu´ vale la pena crear todas eestas funciones nuevas. En realidad hay much´ ısimas razones, pero este ejemplodemuestra dos: Crear una nueva funci´n le da la oportunidad de dar un nombre a un grupo o de sentencias. Las funciones simplifican su programa al ocultar c´lculos a complejos detr´s de ´rdenes sencillas, y usar palabras de su propia lengua a o en vez de c´digo arcano. o Crear una nueva funci´n hace que el programa sea m´s peque˜o, al elimi- o a n nar c´digo repetitivo. Por ejemplo, una manera de imprimir nueve l´ o ıneas consecutivas es llamar a tresLineas tres veces. Como actividad, escriba una funci´n llamada nueveLineas que use o tresLineas para imprimir nueve l´ ıneas en blanco. ¿C´mo impri- o mir´ 27 l´ ıa ıneas nuevas?3.7. Las definiciones y el usoJuntando los fragmentos de c´digo de la secci´n anterior, el programa completo o oqueda de la siguiente manera:
    • 3.8 Flujo de ejecuci´n o 27 def nueva_linea(): print def tresLineas(): nueva_linea() nueva_linea() nueva_linea() print "Primera Linea." tresLineas() print "Segunda Linea."El presente programa contiene dos definiciones de funciones: nueva linea ytresLineas. Las definiciones de funciones se ejecutan como el resto de senten-cias, pero el efecto es crear una nueva funci´n. Las sentencias del interior de ola funci´n no se ejecutan hasta que se llama a la funci´n, y la definici´n de la o o ofunci´n no genera salida. oComo era de esperar, tiene que crear una funci´n antes de poder ejecutarla. oEn otras palabras, la definici´n de la funci´n tiene que ejecutarse antes de la o oprimera vez que se la invoque. Como actividad, pruebe a ejecutar este programa moviendo las tres ultimas sentencias al principio del programa. Registre qu´ mensaje ´ e de error obtiene usted. Como segunda actividad, pruebe a tomar la versi´n del programa o que funcionaba y a mover la definci´n de nueva linea m´s abajo o a que la definici´n de tresLineas . ¿Qu´ ocurre cuando ejecuta el o e programa?3.8. Flujo de ejecuci´n oPara asegurarse de que una funci´n se define antes de su primer uso, tiene que oconocer el orden en el que se ejecutan las sentencias; a esto se le llama flujo deejecuci´n. oLa ejecuci´n comienza siempre por la primera sentencia del programa. Las sen- otencias se ejecutan a raz´n de una cada vez, en orden, hasta que se alcanza una ollamada a una funci´n. oLas definiciones de funciones no alteran el flujo de ejecuci´n del programa, pero orecuerde que las sentencias que hay dentro de la funci´n no se ejecutan hasta oque se hace la llamada a la funci´n. Aunque no es habitual, puede definir una o
    • 28 Funcionesfunci´n dentro de otra. En este caso, la definici´n de funci´n interior no se o o oejecuta hasta que no se llama a la funci´n exterior. oLas llamadas a funciones son como un desv´ en el flujo de ejecuci´n. En lugar ıo ode ir a la siguiente sentencia, el flujo salta hasta la primera l´ ınea de la funci´n oa la que se llama, ejecuta todas las sentencias que encuentre all´ y vuelve a ı,retomar la ejecuci´n en el punto donde lo dej´. o oEsto suena bastante sencillo... hasta que se acuerda de que una funci´n puede ollamar a otra. Mientras estamos en medio de una funci´n, podr´ o ıamos vernosobligados a abandonarla e ir a ejecutar sentencias en otra funci´n m´s. Pero o amientras estamos en esta nueva funci´n, ¡podr´ o ıamos salirnos y ejecutar otrafunci´n m´s! o aAfortunadamente, a Python se le da bien tomar nota de d´nde est´, de manera o aque cada vez que se completa una funci´n, el programa retoma el punto en donde olo dej´ en la funci´n que hizo la llamada. Cuando llega al final del programa, o otermina.¿Cu´l es la moraleja de toda esta historia? Cuando est´ leyendo un programa, a eno lo lea desde la parte superior a la inferior. En lugar de eso, siga el flujo deejecuci´n. o3.9. Par´metros y argumentos aAlgunas de las funciones internas que hemos usado precisan de argumentos, losvalores que controlan c´mo la funci´n lleva a cabo su tarea. Por ejemplo, si o odesea encontrar el seno de un n´mero, tiene que indicar de qu´ n´mero se trata. u e uAs´ pues, sin toma como argumento un valor num´rico. ı eAlgunas funciones toman m´s de un argumento, como pow, que toma dos argu- amentos: la base y el exponente. Dentro de la funci´n, los valores que se le han opasado se asignan a variables llamadas par´metros. aHe aqu´ un ejemplo de una funci´n definida por el usuario, que toma un par´me- ı o atro:def imprimeDoble(paso): print paso, pasoEsta funci´n toma un unico argumento y se lo asigna a un par´metro llamado o ´ apaso. El valor del par´metro (en este punto todav´ no tenemos ni idea de cu´l a ıa aser´) se imprime dos veces, seguido por un car´cter de nueva l´ a a ınea. El nombrepaso se eligi´ para sugerir que el nombre que le d´ a un par´metro depende de o e austed, pero en general es mejor que elija un nombre m´s ilustrativo que paso. a
    • 3.10 Las variables y los par´metros son locales a 29La funci´n imprimeDoble sirve con cualquier tipo (de dato) que se pueda im- oprimir: >>> imprimeDoble(’Jam´n’) o Jam´n Jam´n o o >>> imprimeDoble(5) 5 5 >>> imprimeDoble(3.14159) 3.14159 3.14159En la primera llamada a la funci´n, el argumento es una cadena; en la segunda oes un entero, y en la tercera es un n´mero de coma flotante. uLas mismas reglas de composici´n que se aplican a las funciones internas se oaplican tambi´n a las funciones definidas por el usuario, as´ que puede usar e ıcualquier tipo de expresi´n como argumento de imprimeDoble. o >>> imprimeDoble(’Jam´n’*4) o Jam´nJam´nJam´nJam´n Jam´nJam´nJam´nJam´n o o o o o o o o >>> imprimeDoble(math.cos(math.pi)) -1.0 -1.0Como de costumbre, se eval´a la expresi´n antes de ejecutar la funci´n, de modo u o oque imprimeDoble devuelve Jam´nJam´nJam´nJam´n Jam´nJam´nJam´nJam´n o o o o o o o oen lugar de ’Jam´n’*4’Jam´n’*4. o oAsimismo podemos usar una variable como argumento: >>> latoya = ’Dafne, es mitad laurel mitad ninfa’ >>> imprimeDoble(latoya) Dafne, es mitad laurel mitad ninfa. Dafne, es mitad laurel mitad ninfa.Observe un aspecto realmente importante en este caso: el nombre de la variableque pasamos como argumento (latoya) no tiene nada que ver con el nombre delpar´metro (paso). No importa c´mo se llamaba el valor en su lugar original (el a olugar desde donde se invoc´); aqu´ en imprimeDoble llamamos a todo el mundo o ıpaso.3.10. Las variables y los par´metros son locales aCuando crea una variable dentro de una funci´n, s´lo existe dentro de dicha o ofunci´n, y no puede usarla fuera de ella. Por ejemplo, la funci´n o o >>> def catDoble(parte1, parte2): ... cat = parte1 + parte2 ... imprimeDoble(cat) ... >>>
    • 30 Funcionestoma dos argumentos, los concatena y luego imprime el resultado dos veces.Podemos llamar a la funci´n con dos cadenas: o >>> cantus1 = "Die Jesu domine, " >>> cantus2 = "Dona eis requiem." >>> catDoble(cantus1, cantus2) Die Jesu domine, Dona eis requiem. Die Jesu domine, Dona eis requiem.Cuando catDoble termina, la variable cat se destruye. Si trat´semos de impri- amirla, obtendr´ ıamos un error: >>> print cat NameError: catLos par´metros tambi´n son locales. Por ejemplo, una vez fuera de la funci´n a e oimprimeDoble, no existe nada llamado paso. Si trata de usarla, Python se que-jar´. a3.11. Diagramas de pilaPara mantener el rastro de qu´ variables pueden usarse y d´nde, a veces es util e o ´dibujar un diagrama de pila. Como los diagramas de estado, los diagramasde pila muestran el valor de cada variable, pero tambi´n muestran la funci´n a e ola que cada variable pertenece.Cada funci´n se representa por una caja con el nombre de la funci´n junto a o o´l. Los par´metros y variables que pertenecen a una funci´n van dentro. Pore a oejemplo, el diagrama de stack para el programa anterior tiene este aspecto: __main__ chant1 "Pie Jesu domine," chant2 "Dona eis requiem." catTwice part1 "Pie Jesu domine," part2 "Dona eis requiem." cat "Pie Jesu domine, Dona eis requiem." printTwice bruce "Pie Jesu domine, Dona eis requiem."El orden de la pila muestra el flujo de ejecuci´n. imprimeDoble fue llamado opor catDoble y a catDoble lo invoc´ main , que es un nombre especial de la o
    • 3.12 Funciones con resultado 31funci´n m´s alta. Cuando crea una variable fuera de cualquier funci´n, pertenece o a oa main .En cada caso, el par´metro se refiere al mismo valor que el argumento corres- apondiente. As´ que parte1 en catDoble tiene el mismo valor que cantus1 en ı main .Si sucede un error durante la llamada a una funci´n, Python imprime el nombre ode la funci´n y el nombre de la funci´n que la llam´, y el nombre de la funci´n o o o oque llam´ a ´sa, y as´ hasta main . o e ıPor ejemplo, si intentamos acceder a cat desde imprimeDoble, provocaremosun NameError:Traceback (innermost last): File "test.py", line 13, in __main__ catDoble(cantus1, cantus2) File "test.py", line 5, in catDoble imprimeDoble(cat) File "test.py", line 9, in imprimeDoble print catNameError: catEsta lista de funciones de llama traceback (traza inversa). Le dice a usted enqu´ archivo de programa sucedi´ el error, y en qu´ l´ e o e ınea, y qu´ funciones se eejecutaban en ese momento. Tambi´n muestra la l´ e ınea de c´digo que caus´ el o oerror. ıjese en la similaridad entre la traza inversa y el diagrama de pila. No es unaF´coincidencia.3.12. Funciones con resultadoSeguramente ha notado ya que algunas de las funciones que estamos usando,igual que las funciones matem´ticas, devuelven un resultado. Otras funciones, acomo nueva linea, llevan a cabo una acci´n pero no devuelven un valor. Ello osuscita varias preguntas: 1. ¿Qu´ sucede si llama usted a uana funci´n y no hace nada con el resultado e o (es decir, no lo asigna a una variable ni lo usa como parte de una expresi´n o m´s amplia)? a 2. ¿Qu´ sucede si usa una funci´n sin resultado como parte de una expresi´n, e o o por ejemplo nueva linea() + 7?
    • 32 Funciones 3. ¿Se pueden escribir funciones que devuelvan resultados, o debemos limi- tarnos a funcinoes simples como nueva linea e imprimeDoble?La respuesta a la tercera pregunta es “s´ puede escribir funciones que devuelvan ı,valores”, y lo haremos en el cap´ ıtulo 5. Como actividad final, consteste a las otras dos preguntas intentando hacerlas en la pr´ctica. Cada vez que tenga una duda sobre lo que a es legal o ilegal en Python, perguntar al int´rprete ser´ una buena e a manera de averiguarlo.3.13. Glosariollamada a funci´n: Una sentencia que ejecuta una funci´n. Est´ compuesta o o a por el nombre de la funci´n m´s una lista de argumentos encerrados entre o a par´ntesis. eargumento: Valor que se le pasa a una funci´n cuando se la llama. El valor se o asigna al par´metro correspondiente de la funci´n. a ovalor de retorno: Es el resultado de una funci´n. Si se usa una llamada a o funci´n a modo de expresi´n, el valor de retorno es el valor de la expresi´n. o o oconversi´n de tipo: Una sentencia expl´ o ıcita que toma un valor de un tipo y calcula el valor correspondiente de otro tipo.coerci´n: Conversi´n tipos que ocurre autom´ticamente de acuerdo con las o o a reglas de coerci´n de Python. om´dulo: Fichero que contiene una colecci´n de funciones y clases relacionadas. o onotaci´n de punto: La sintaxis para llamar a una funci´n de otro m´dulo, o o o especificando el nombre del m´dulo, seguido por un punto y el nombre de o la funci´n. ofunci´n: Secuencia de sentencias etiquetadas que llevan a cabo determinada o operaci´n de utilidad. Las funciones pueden tomar par´metros o no, y o a pueden producir un resultado o no.definici´n de funci´n: Sentencia que crea una nueva funci´n, especificando o o o su nombre, par´metros y las sentencias que ejecuta. aflujo de ejecuci´n: Orden en el que se ejecutan las sentencias durante la eje- o cuci´n de un programa. o
    • 3.13 Glosario 33par´metro: Nombre que se usa dentro de una funci´n para referirse a el valor a o que se le pasa como argumento.variable local: variable definida dentro de una funci´n. Las variables locales o s´lo pueden usarse dentro de su funci´n. o odiagrama de pila: Representaci´n gr´fica de una pila de funciones, sus varia- o a bles y los valores a los que se refieren.traza inversa: (traceback en ingl´s) Una lista de las funciones en curso de e ejecuci´n, presentadas cuando sucede un error en tiempo de ejecuci´n. o o notaci´n de punto o traza inversa
    • Cap´ ıtulo 4Condicionales yrecursividad4.1. El operador m´dulo oEl operador m´dulo funciona con enteros (y expresiones enteras), y devuelve oel resto de dividir el primer operando entre el segundo. En Python, el operadorde m´dulo es el signo de tanto por ciento ( %). La sintaxis es la misma de los ootros operadores:>>> cociente = 7 / 3>>> print cociente2>>> resto = 7 % 3>>> print resto1As´ 7 dividido entre 3 da 2 con 1 de resto. ı,El operador de m´dulo resulta ser soprendentemente util. Por ejemplo, puede o ´comprobar si un n´mero es divisible entre otro: si x % y es cero, entonces x es udivisible entre y.Tambi´n puede usar el operador m´dulo para extraer el d´ e o ıgito m´s a la derecha ade un n´mero. Por ejemplo, x % 10 devuelve el d´ u ıgito m´s a la derecha de x (en abase 10). De forma similar, x % 100 devuelve los dos ultimos d´ ´ ıgitos.
    • 36 Condicionales y recursividad4.2. Expresiones booleanasUna expresi´n booleana es una expresi´n que es cierta o falsa. En Python, o ouna expresi´n que es cierta tiene el valor 1, y una expresi´n que es falsa tiene o oel valor 0.El operador == compara dos valores y entrega una expresi´n booleana: o>>> 5 == 51>>> 5 == 60En la primera sentencia, los dos operandos son iguales, as´ que la expresi´n se ı oeval´a como 1 (verdadero); en la segunda sentencia, 5 no es igual a 6, as´ que u ıobtenemos 0 (falso).El operador == es uno de los operadores de comparaci´n; los otros son: o x != y # x no es igual a y x > y # x es mayor que y x < y # x es menor que y x >= y # x es mayor o igual que y x <= y # x es menor o igual que yAunque probablemente estas operaciones le resulten familiares, los s´ımbolos enPython son diferentes de los matem´ticos. Un error habitual es utilizar un signo aigual sencillo (=) en lugar del doble (==). Recuerde que = es un operador deasignaci´n y == es un operador de comparaci´n. Adem´s, no existen =< ni =>. o o a4.3. Operadores l´gicos oHay tres operadores l´gicos: and, or, y not. La sem´ntica (significado) de o aestos operadores es similar a sus significados en ingl´s. Por ejemplo, x >0 and ex <10 es verdadero s´lo si x es mayor que 0 y menor que 10. on %2 == 0 or n %3 == 0 es verdadero si cualquiera de las condiciones es verda-dera, o sea, si el n´mero es divisible por 2 o por 3. uFinalmente, el operador not niega una expresi´n booleana, de forma que not(x o>y) es cierto si (x >y) es falso, o sea, si x es menor o igual que y.Hablando estrictamente, los operandos de los operadores l´gicos deber´ ser o ıanexpresiones booleanas, pero Python no es muy estricto. Cualqueir n´mero que uno sea cero se interpreta como “verdadero”.
    • 4.4 Ejecuci´n condicional o 37>>> x = 5>>> x and 11>>> y = 0>>> y and 10En general, este tipo de cosas no se considera buen estilo. Si quiere compararun valor con cero, deber´ hacerlo expl´ ıa ıcitamente.4.4. Ejecuci´n condicional oPara escribir programas utiles, casi siempre necesitamos la capacidad de com- ´probar ciertas condiciones y cambiar el comportamiento del programa en conso-nancia. Las sentencias condicionales nos dan esta capacidad. La forma m´s asencilla es la sentencia if: if x > 0: print "x es positivo"La expresi´n booleana tras el if se llama condici´n. Si es verdadera, entonces o ola sentencia indentada se ejecuta. Si la condici´n no es verdadera, no pasa nada. oComo otras sentencias compuestas, if consta de una cabecera y un bloque desentencias:CABECERA: PRIMERA SENTENCIA ... ULITMA SENTENCIALa cabecera comienza con una nueva l´ ınea y termina con el signo de dos puntos.Los elementos indentados que siguen se llaman bloque de la sentencia. Laprimera sentencia no indentada marca el fin del bloque. Un bloque de sentenciasdentro de una sentencia compuesta recibe el nombre de cuerpo de la sentencia.No hay l´ımite a la cantidad de sentencias que pueden aparecer en el cuerpo deuna sentencia if, pero debe haber al menos una. A veces, es util tener un cuerpo ´sin sentencias, (normalmente como reserva de espacio para algo de c´digo que otodav´ no ha escrito). En tales casos, puede usted utilizar la sentencia pass, ıaque no hace nada.4.5. Ejecuci´n alternativa oUna segunda forma de la sentencia if es la ejecuci´n alternativa, en la que hay odos posibilidades, y la condici´n determina cu´l de ellas se ejecuta. La sintaxis o a
    • 38 Condicionales y recursividadtiene este aspecto: if x%2 == 0: print x, "es par" else: print x, "es impar"Si el resto cuando se divide x entre 2 es cero, entonces sabemos que x es par,y este programa muestra un mensaje a tal efecto. Si la condici´n es falsa, se oejecuta el segundo lote de sentencias. Puesto que la condici´n debe ser verdadera oo falsa, se ejecutar´ exactamente una de las alternativas. Llamamos ramas a alas posibilidades porque son ramas del flujo de ejecuci´n. oComo un aparte, si piensa que querr´ comprobar con frecuencia la paridad de an´meros, quiz´ desee “envolver” este c´digo en una funci´n: u a o o def imprimeParidad(x): if x%2 == 0: print x, "es par" else: print x, "es impar"Ahora tiene una funci´n llamada imprimeParidad que muestra el mensaje apro- opiado para cada n´mero entero que usted le pase. Llame a esta funci´n de la u omanera siguiente:>>> imprimeParidad(17)>>> imprimeParidad(y+1)4.6. Condiciones encadenadasA veces hay m´s de dos posibilidades y necesitamos m´s de dos ramas. Una a aforma de expresar tal computaci´n es un conditional encadenado: o if x < y: print x, "es menor que", y elif x > y: print x, "es mayor que", y else: print x, "y", y, "son iguales"elif es una abreviatura de ”else if”. De nuevo, s´lo se ejecutar´ una rama. No o ahay l´ ımite al n´mero de sentencias elif, pero s´lo se permite una sentencia u oelse (que puede omitirse) y debe ser la ultima rama de la sentencia: ´ if eleccion == ’A’: funcionA() elif eleccion == ’B’:
    • 4.7 Condiciones anidadas 39 funcionB() elif eleccion == ’C’: funcionC() else: print "Eleccion no valida."Las condiciones se comprueban en orden. Si la primera es falsa, se compruebala siguiente, y as´ Si una de ellas es cierta, se ejecuta la rama correspondiente ı.y termina la sentencia. Incluso si es cierta m´s de una condici´n, s´lo se ejecuta a o ola primera rama verdadera. Como ejercicio, envuelva estos ejemplos en funciones llamadas compara(x, y) y resuelve(eleccion).4.7. Condiciones anidadasUna condici´n puede estar anidada dentro de otra. Pod´ o ıamos haber escrito as´ el ıejemplo de tricotom´ ıa: ~~if x == y: ~~~~print x, "y", y, "son iguales" ~~else: ~~~~if x < y: ~~~~~~print x, "es menor que", y ~~~~else: ~~~~~~print x, "es mayor que", yLa condici´n externa que contiene dos ramas. La primera rama contiene una osentencia simple de salida. La segunda rama contiene otra sentencia if, quetiene dos ramas en s´ misma. Estas dos ramas son ambas sentencias de salida ıde datos, aunque podr´ ser igualmente sentencias condicionales. ıanAunque la indentaci´n de las sentencias hace la estructura evidente, las condi- ociones anidadas en seguida se vuelven dif´ ıciles de leer. En general es una buenaidea evitarlas cuando pueda.Los operadores l´gicos suelen facilitar un modo de simplificar las sentencias ocondicionales anidadas. Por ejemplo, podemos reescribir el c´digo siguiente con oun s´lo condicional: oif 0 < x: if x < 10: print "x es un n´mero positivo de un d´gito." u ıLa sentencia print s´lo se ejecuta si conseguimos superar ambos condicionales, oas´ que podemos usar el operador and: ı
    • 40 Condicionales y recursividadif 0 < x and x < 10: print "x es un n´mero positivo de un d´gito." u ıEstos tipos de condiciones son habituales, por lo que Python nos proporcionauna sintaxis alternativa similar a la notaci´n matem´tica: o aif 0 < x < 10: print "x es un n´mero positivo de un d´gito." u ıEsta condici´n es sem´nticamente la misma que la expresi´n booleana compues- o a ota y que el condicional anidado.4.8. La sentencia returnLa sentencia return le permite terminar la ejecuci´n de una funci´n antes de o oalcanzar su final. Una raz´n para usarla es detectar una condici´n de error: o o import math def imprimeLogaritmo(x): if x <= 0: print "Solo numeros positivos, por favor." return result = math.log(x) print "El log de x es", resultLa funci´n imprimeLogaritmo toma un par´metro llamado x. Lo primero que o ahace es comprobar si x es menor o igual que cero, en cuyo caso muestra unmensaje de error y luego usa return para salir de la funci´n. El flujo de la oejecuci´n vuelve inmediatamente al llamante y no se ejecutan las l´ o ıneas restantesde la funci´n. oRecuerde que para usar una funci´n del m´dulo math tiene que importarlo. o o4.9. RecursividadYa mencionamos que es legal que una funci´n llame a otra, y de ello hemos visto oya varios ejemplos. Olvidamos mencionar que tambi´n es legal que una funci´n e ose llame a s´ misma. Puede no resultar evidente por qu´ es bueno esto, pero ı eviene a resultar una de las cosas m´s interesantes y curiosas que puede hacer aun programa. Examine por ejemplo la siguiente funci´n: o
    • 4.9 Recursividad 41 def cuenta_atras(n): if n == 0: print "Despegando!" else: print n cuenta_atras(n-1)cuenta atras espera que su par´metro, n, sea un entero positivo. Si n el par´me- a atro es cero, muestra la palabra “Despegando!”. En otro caso, muestra n y luegollama a la funci´n llamada cuenta atras (ella misma) pas´ndole como argu- o amento n-1.¿Qu´ sucede si llamamos a la funci´n de la siguiente manera? e o>>> cuenta_atras(3)La ejecuci´n de cuenta atras comienza con n=3, y puesto que n no es cero, da ocomo salida el valor 3, y luego se llama a s´ misma ... ı La ejecuci´n de cuenta atras comienza con n=2, y puesto que n no o es cero, muestra el valor 2 y luego se llama a s´ misma ... ı La ejecuci´n de cuenta atras comienza con n=1, y puesto o que n no es cero, muestra el valor 1, y luego se llama a s´ misma... ı La ejecuci´n de cuenta atras comienza con n=0, o y puesto que n es cero, muestra la palabra “Des- pegando!” y luego retorna. La cuenta atras que dio n=1 retorna. La cuenta atras que dio n=2 retorna.La cuenta atras que dio n=3 retorna.Y entonces ya est´ de vuelta en main (menudo viaje). De manera que la asalida completa presenta el siguiente aspecto:321Despegando!Como segundo ejemplo, consideremos de nuevo las funciones nuevaLinea andtresLineas. def nuevaLinea(): print
    • 42 Condicionales y recursividad def tresLineas(): nuevaLinea() nuevaLinea() nuevaLinea()Aunque todas funcionan, no ser´ de mucha ayuda si quisiera mostrar 2 l´ ıan ıneasnuevas o 106. Una mejor alternativa ser´: a def nLineas(n): if n > 0: print nLineas(n-1)Este programa es parecido a cuenta atras; mientras n sea mayor que cero,muestra una nueva l´ ınea, y luego se llama a s´ misma para mostrar >n-1 nuevas ıl´ ıneas m´s. De esta manera, el n´mero total de nuevas l´ a u ıneas es 1 + (n-1), quesi rescata su ´lgebra ver´ que es n. a aEl proceso por el que una funci´n se llama a s´ misma se llama recursividad, o ıy dichas funciones se denominan recursivas.4.10. Diagramas de pila para funciones recursi- vasEl la Secci´n 3.11 utilizamos un diagrama de pila para representar el estado de oun programa durante la llamada de una funci´n. El mismo tipo de diagrama opuede hacer m´s f´cil interpretar una funci´n recursiva. a a oCada vez que se llama a una funci´n, Python crea un nuevo marco para la ofunci´n, que contiene sus variables locales y par´metros. En el caso de una o afunci´n recursiva, puede haber m´s de un marco en la pila al mismo tiempo. o aLa figura muestra un diagrama de pila para cuenta atras, invocada con n =3:
    • 4.11 Recursividad infinita 43 __main__ countdown n 3 countdown n 2 countdown n 1 countdown n 0Como es habitual, en lo alto de la pila est´ el marco de main . Est´ vac´ a a ıaporque no hemos ninguna variable sobre main ni le hemos pasado ning´n upar´metro. aLos cuatro marcos de cuenta atras tienen valores diferentes para el par´metro an. El fondo de la pila, donde n=0, se llama caso base. No hace una llamadarecursiva, de manera que no hay m´s marcos. a Como actividad, dibuje un diagrama de pila para nLineas, invocada con el par´metro n=4. a4.11. Recursividad infinitaSi una recursi´n no alcanza nunca el caso base, seguir´ haciendo llamadas re- o acursivas para siempre y nunca terminar´. Esta circunstancia se conoce como arecursividad infinita, y generalmente no se la considera una buena idea. Estees un programa m´ ınimo con recursividad infinita:def recurre(): recurre()El la mayor´ de los entornos de programaci´n, un programa con recursividad ıa oinfinita no se ejecutar´ realmente para siempre. Python informar´ de un mensaje a ade error cuando se alcance el nivel m´ximo de recursividad: a File "<stdin>", line 2, in recurse (98 repetitions omitted) File "<stdin>", line 2, in recurseRuntimeError: Maximum recursion depth exceeded
    • 44 Condicionales y recursividadEsta traza inversa es un poco mayor que la que vimos en el cap´ ıtulo anterior.¡Cuando sucede el error, hay 100 marcos recurre en la pila! Como actividad, escriba una funci´n con recursividad infinita y o ejec´tela en el int´rprete de Python. u e4.12. Entrada por tecladoLos programas que hemos escrito hasta ahora son un poco maleducados en elsentido de que no aceptan entradas de datos del usuario. Simplemente hacen lomismo siempre.Python proporciona funciones internas que obtienen entradas desde el teclado.La m´s sencilla se llama raw input. Cuando llamamos a esta funci´n, el pro- a ograma se detiene y espera a que el usuario escriba algo. Cuando el usuario pulsala tecla Return o Enter, el programa se reanuda y raw input devuelve lo queel usuario escribi´ como tipo string: o >>> entrada = raw_input () A qu´ est´s esperando? e a >>> print entrada A qu´ est´s esperando? e aAntes de llamar a raw input es conveniente mostrar un mensaje que le pida alusuario el dato solicitado. Este mensaje se llama indicador (prompt en ingl´s). ePuede proporcionarle un indicador a raw input como argumento: >>> nombre = raw_input ("C´mo te llamas? ") o C´mo te llamas? H´ctor, h´roe de los Troyanos! o e e >>> print nombre H´ctor, h´roe de los Troyanos! e eSi espera que la entrada sea un entero, utilice la funci´n input. Por ejemplo: o >>> indicador = ... "Cu´l es la velocidad de una golondrina sin carga?n" a >>> velocidad = input (indicador)Si el usuario teclea una cadena de n´meros, se convertir´ en un entero y se u aasignar´ a velocidad. Por desgracia, si el usuario escribe algo que no sea un ad´ıgito, el programa dar´ un error: a >>> velocidad = input (indicador) Cu´l es la velocidad de una golondrina sin carga? a Se refiere usted a la golondrina europea o a la africana? SyntaxError: invalid syntaxPara evitar este tipo de error, generalmente es buena idea usar raw input paraobtener una cadena y usar entonces las funciones de conversi´n para convertir oa otros tipos.
    • 4.13 Glosario 454.13. Glosariooperador m´dulo: Operador, se˜alado con un signo de tanto por ciento ( %), o n que trabaja sobre enteros y devuelve el resto cuando un n´mero se divide u entre otro.expresi´n booleana: Una exprersi´n que es cierta o falsa. o ooperador de comparaci´n: Uno de los operadores que comparan dos valores: o ==, !=, >, <, >= y <=.operador l´gico: Uno de los operadores que combinan expresiones booleanas: o and, or y not.sentencia condicional: Sentencia que controla el flujo de ejecuci´n de un pro- o grama dependiendo de cierta condici´n. ocondici´n: La expresi´n booleana de una sentencia condicional que determina o o qu´ rama se ejecutar´. e asentencia compuesta: Estructura de Python que est´ formado por una cabe- a cera y un cuerpo. La cabecera termina en dos puntos (:). El cuerpo tiene una sangr´ con respecto a la cabecera. ıabloque: Grupo sentencias consecutivas con el mismo sangrado.cuerpo: En una sentencia compuesta, el bloque de sentencias que sigue a la cabecera de la sentencia.anidamiento: Una estructura de programa dentro de otra; por ejemplo, una sentencia condidional dentro de una o ambas ramas de otra sentencia condicional.recursividad: El proceso de volver a llamar a la funci´n que se est´ ejecutando o a en ese momento.caso base: En una funci´n recursiva, la rama de una sentencia condicional que o no ocasiona una llamada recursiva.recursividad infinita: Funci´n que se llama a s´ misma recursivamente sin o ı alcanzar nunca el caso base. A la larga una recursi´n infinita provocar´ un o a error en tiempo de ejecuci´n. oindicador: indicador visual que invita al usuario a introducir datos.
    • Cap´ ıtulo 5Funciones productivas5.1. Valores de retornoAlgunas de las funciones internas que hemos usado, como las funciones math ofunciones matem´ticas, han producido resultados. Llamar a la funci´n genera un a onuevo valor, que normalmente asignamos a una variable pasa usar como partede una expresi´n. o import math e = math.exp(1.0) altura = radio * math.sin(angulo)Pero hasta ahora, ninguna de las funciones que hemos escrito ha devuelto unvalor.En este cap´ ıtulo escribiremos funciones que devuelvan valores, que llamaremosfunciones productivas, a falta de un nombre mejor. El primer ejemplo esarea, que devuelve el ´rea de un c´ a ırculo con un radio dado: import math def area(radio): temporal = math.pi * radio**2 return temporalYa hemos visto antes la sentencia return, pero en una funci´n productiva la osentencia return incluye un valor de retorno. Esta sentencia quiere decir “re-torna inmediatamente de la funci´n y usa la siguiente expresi´n como valor de o oretorno”. La expresi´n dada puede ser arbitrariamente complicada; as´ pues, o ıpodr´ıamos haber escrito esta funci´n m´s concisamente: o a
    • 48 Funciones productivas def area(radio): return math.pi * radio**2Por otra parte, las variables temporales como temporal suelen hacer m´s af´cil el depurado. aA veces es util disponer de varias sentencias de retorno, una en cada rama de ´una condici´n: o def valorAbsoluto(x): if x < 0: return -x else: return xPuesto que estas sentencias return est´n en una condici´n alternativa, s´lo se a o oejecutar´ una de ellas. En cuanto se ejecuta una de ellas, la funci´n termina sin a oejecutar ninguna de las sentencias siguientes.El c´digo que aparece despu´s de una sentencia return o en cualquier otro lugar o edonde el flujo de ejecuci´n no pueda llegar, recibe el nombre de c´digo muerto. o oEn una funci´n productiva es una buena idea asegurarse de que cualquier posible orecorrido del programa alcanza una sentencia return. Por ejemplo: def valorAbsoluto(x): if x < 0: return -x elif x > 0: return xEste programa no es correcto porque si resulta que x vale 0, entonces no se cum-ple ninguna de ambas condiciones y la funci´n termina sin alcanzar la setencia oreturn. En este caso, el valor de retorno es un valor especial llamado None: >>> print valorAbsoluto(0) None Como actividad, escriba una funci´n comparar que devuelva 1 si x o >y , 0 si x == y , y -1 si x <y .5.2. Desarrollo de programasLlegados a este punto, tendr´ que poder mirar a funciones Python completas ıay adivinar qu´ hacen. Tambi´n, si ha hecho los ejercicios, habr´ escrito algu- e e anas funcioncillas. Tal como vaya escribiendo funciones mayores puede empezar
    • 5.2 Desarrollo de programas 49a experimentar m´s dificultades, especialmente con los errores en tiempo de aejecuci´n y los sem´nticos. o aPara lidiar con programas de complejidad creciente, vamos a sugerirle una t´cni- eca que llamaremos desarrollo incremental. El objetivo del desarrollo incre-mental es sustituir largas sesiones de depuraci´n por la adici´n y prueba de o opeque˜as porciones de c´digo en cada vez. n oPor ejemplo, supongamos que desea encontrar la distancia entre dos puntos, da-dos por las coordenadas (x1 , y1 ) y (x2 , y2 ). Por el teorema de Pit´goras, podemos aescribir la distancia es: distancia = (x2 − x1 )2 + (y2 − y1 )2 (5.1)El primer paso es considerar qu´ aspecto tendr´ una funci´n distancia en e ıa oPython. En otras palabras, ¿cu´les son las entradas (par´metros) y cu´l es la a a asalida (valor de retorno)?En este caso, los dos puntos son los par´metros, que podemos representar usando acuatro par´metros. El valor de retorno es la distancia, que es un valor en coma aflotante.Ya podemos escribir un bosquejo de la funci´n: o def distancia(x1, y1, x2, y2): return 0.0Obviamente, la funci´n no calcula distancias; siempre devuelve cero. Pero es osint´cticamente correcta y se ejecutar´, lo que significa que podemos probarla a aantes de complicarla m´s. aPara comprobar la nueva funci´n, tenemos que llamarla con valores de ejemplo: o >>> def distancia(x1, y1, x2, y2): ... return 0.0 ... >>> distancia(1, 2, 4, 6) 0.0 >>>Elegimos estos valores de tal forma que la distancia horizontal sea igual a 3 y ladistancia vertical sea igual a 4; de esa manera el resultado es 5 (la hipotenusa deltri´ngulo 3-4-5). Cuando se comprueba una funci´n, es util saber la respuesta a o ´correcta.Hasta el momento, hemos comprobado que la funci´n es sint´cticamente correc- o ata, as´ que podemos empezar a a˜adir l´ ı n ıneas de c´digo. Despu´s de cada cambio o e
    • 50 Funciones productivasincremental, comprobamos de nuevo la funci´n. Si en un momento dado apare- oce un error, sabremos d´nde est´ exactamente: en la ultima l´ o a ´ ınea que hayamosa˜adido. nEl siguiente paso en el c´lculo es encontrar las diferencias entre x2 −x1 y y2 −y1 . aAlmacenaremos dichos valores en variables temporales llamadas dx y dy y lasimprimiremos. def distancia(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 print "dx es", dx print "dy es", dy return 0.0Si la funci´n funciona, valga la redundancia, las salidas deber´ ser 3 y 4. Si o ıanes as´ sabemos que la funci´n recibe correctamente los par´metros y realiza ı, o acorrectamente el primer c´lculo. Si no, s´lo hay unas pocas l´ a o ıneas que revisar.Ahora calculamos la suma de los cuadarados de dx y dy: def distancia(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 dalcuadrado = dx**2 + dy**2 print "dalcuadrado es: ", dalcuadrado return 0.0F´ıjese en que hemos eliminado las sentencias print que escribimos en el pa-so anterior. Este c´digo se llama andamiaje porque es util para construir el o ´programa pero no es parte del producto final.De nuevo querremos ejecutar el programa en este estado y comprobar la salida(que deber´ dar 25). ıaPor ultimo, si hemos importado el m´dulo math, podemos usar la funci´n sqrt ´ o opara calcular y devolver el resultado: def distancia(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 dalcuadrado = dx**2 + dy**2 resultado = math.sqrt(dalcuadrado) return resultadoSi esto funciona correctamente, ha terminado. Si no, podr´ ser que quisiera ıausted imprimir el valor de resultado antes de la sentencia return.
    • 5.3 Composici´n o 51Al principio, deber´ a˜adir solamente una o dos l´ ıa n ıneas de c´digo cada vez. oConforme vaya ganando experiencia, puede que se encuentre escribiendo y de-purando trozos mayores. Sin embargo, el proceso de desarrollo incremental puedeahorrarle mucho tiempo de depurado.Los aspectos fundamentales del proceso son: 1. Comience con un programa que funcione y h´gale peque˜os cambios in- a n crementales. Si hay un error, sabr´ exactamente d´nde est´. a o a 2. Use variables temporales para mantener valores intermedios, de tal manera que pueda mostrarlos por pantalla y comprobarlos. 3. Una vez que el programa est´ funcionando, tal vez prefiera eliminar parte e del andamiaje o aglutinar m´ltiples sentencias en expresiones compuestas, u pero s´lo si eso no hace que el programa sea dif´ de leer. o ıcil Como actividad, utilice el desarrollo incremental para escribir una funci´n de nombre hipotenusa que devuelva la longitud de la hipo- o tenusa de un tri´ngulo rect´ngulo, dando como par´metros los dos a a a catetos. Registre cada estado del desarrollo incremental seg´n vaya u avanzando.5.3. Composici´n oComo seguramente a estas alturas ya supondr´, se puede llamar a una funci´n a odesde dentro de otra. Esta habilidad se llama composici´n . oComo ejemplo, escribiremos una funci´n que tome dos puntos, el centro del oc´ ırculo y un punto del per´ ımetro, y calcule el ´rea del c´ a ırculo.Supongamos que el punto central est´ almacenado en las variables xc e yc, y aque el punto del per´ ımetro lo est´ en xp e yp. El primer paso es hallar el radio adel c´ ırculo, que es la distancia entre los dos puntos. Afortunadamente hay unafunci´n, distancia, que realiza esta tarea: o radio = distancia(xc, yc, xp, yp)El segundo paso es encontrar el ´rea de un c´ a ırculo con ese radio y devolverla: resultado = area(radio) return resultadoEnvolviendo todo esto en una funci´n, obtenemos: o def area2(xc, yc, xp, yp): radio = distancia(xc, yc, xp, yp) resultado = area(radio) return resultado
    • 52 Funciones productivasHemos llamado a esta funci´n area2 para distinguirla de la funci´n area definida o oanteriormente. S´lo puede haber una unica funci´n con un determinado nombre o ´ odentro de un m´dulo. oLas variables temporales radio y area son utiles para el desarrollo y el depu- ´rado, pero una vez que el programa est´ funcionando, podemos hacerlo m´s a aconciso integrando las llamadas a las funciones en una sola l´ ınea: def area2(xc, yc, xp, yp): return area(distancia(xc, yc, xp, yp)) Como actividad, escriba una funci´n pendiente(x1, y1, x2, y2) o que devuelva la pendiente de la l´ınea que atraviesa los puntos (x1, y1) y (x2, y2). Luego use esta funci´n en una funci´n que se llame o o intercepta(x1, y1, x2, y2) que devuelva la [[y-intercepta]] de la ınea a trav´s de los puntos (x1, y1) y (x2, y2). l´ e5.4. Funciones booleanasLas funciones pueden devolver valores booleanos, lo que a menudo es convenientepara ocultar complicadas comprobaciones dentro de funciones. Por ejemplo: def esDivisible(x, y): if x % y == 0: return 1 # it’s true else: return 0 # it’s falseLa funci´n lleva por nombre esDivisible. Es habitual dar a las funciones boo- oleanas nombres que suenan como preguntas s´ı/no. Devuelve 1 ´ 0 para indicar osi la x es o no divisibelo por y.Podemos reducir el tama˜o de la funci´n aprovech´ndonos del hecho de que n o ala sentencia condicional que hay despu´s del if es en s´ misma una expresi´n e ı obooleana. Podemos devolverla directamente, evitando a la vez la sentencia if: def esDivisible(x, y): return x % y == 0La siguiente sesi´n muestra a la nueva funci´n en acci´n: o o o>>> esDivisible(6, 4)0>>> esDivisible(6, 3)1El uso m´s com´n para las funciones booleanas es dentro de sentencias condi- a ucionales:
    • 5.5 M´s recursividad a 53 if esDivisible(x, y): print "x es divisible entre y" else: print "x no es divisible entre y"Puede parecer tentador escribir algo como:if esDivisible(x, y) == 1:Pero la comparaci´n extra es innecesaria. o Como actividad, escriba una funci´n estaEntre(x, y, z) que de- o vuelva 1 en caso de que y <= x <= z y que devuelva 0 en cualquier otro caso.5.5. M´s recursividad aHasta ahora, usted ha aprendido solamente un peque˜o subconjunto de Python, npero puede que le interese saber que ese subconjunto es ya un lenguaje deprogramaci´n completo; con esto queremos decir que cualquier cosa que pueda ocomputarse se puede expresar en este lenguaje. Cualquier programa que se hayaescrito alguna vez puede reescribirse utilizando unicamente las caracter´ ´ ısticasdel lenguaje que ha aprendido hasta el momento (de hecho, necesitar´ algunas ıa´rdenes para controlar dispositivos como el teclado, el rat´n, los discos, etc, peroo oeso es todo).Probar tal afirmaci´n es un ejercicio nada trivial, completado por primera vez opor Alan Turing, uno de los primeros cient´ ıficos inform´ticos (algunos argumen- atar´n que era un matem´tico, pero muchos de los cient´ a a ıficos inform´ticos pione- aros comenzaron como matem´ticos). En correspondencia, se la conoce como la atesis de Turing. Si estudia un curso de Teor´ de la Computaci´n, tendr´ opor- ıa o atunidad de ver la prueba.Para darle una idea de lo que puede hacer con las herramientas que ha aprendidohasta ahora, evaluaremos una serie de funciones matem´ticas que se definen arecursivamente. Una definici´n recursiva es semejante a una definici´n circular, o oen el sentido de que la definici´n contiene una referencia a lo que se define. Una odefinici´n verdaderamente circular no es muy util: o ´frangoso: adjetivo que describe algo que es frangosoSi usted viera esa definici´n en el diccionario, se quedar´ confuso. Por otra parte, o ıasi ha buscado la definici´n de la funci´n matem´tica factorial, habr´ visto algo o o a asejemante a lo siguiente:
    • 54 Funciones productivas 0! = 1 n! = n · (n − 1)!Esta definici´n establece que el factoral de 0 es 1, y que el factorial de cualquier ootro valor, n, es n multiplicado por el factorial de n − 1.As´ pues, 3! es 3 veces 2!, que es 2 veces 1!, que es una vez 0!. Junt´ndolos todos, ı a, 3! es igual a 3 veces 2 veces 1 vez 1, que es 6.Si puede escribir una definici´n recursiva de algo, normalmente podr´ escribir o aun programa de Python para evaluarlo. El primer paso es decidir cu´les son los apar´metros para esta funci´n. Con poco esfuerzo llegar´ a la conclusi´n de que a o a ofactorial toma un unico par´metro: ´ a def factorial(n):Si resultase que el argumento fuese 0, todo lo que hemos de hacer es devolver 1: def factorial(n): if n == 0: return 1En otro caso, y he aqu´ la parte interesante, tenemos que hacer una llamada ırecursiva para hallar el factorial de n − 1 y luego multiplicarlo por n: def factorial(n): if n == 0: return 1 else: recursivo = factorial(n-1) resultado = n * recursivo return resultadoEl flujo de ejecuci´n de este programa es similar al de cuenta atras de la oSecci´n 4.9. Si llamamos a factorial con el valor 3: oPuesto que 3 no es 0, tomamos la segunda rama y calculamos el factorial den-1... Puesto que 2 no es 0, tomamos la segunda rama y calculamos el factorial de n-1... Puesto que 1 no es 0, tomamos la segunda rama y calcu- lamos el factorial de n-1... Puesto que 0 es 0, tomamos la primera rama y devolvemos el valor 1 sin hacer m´s llamadas re- a cursivas.
    • 5.6 Acto de fe 55 El valor de retorno (1) se multiplica por n, que es 1, y se devuelve el resultado. El valor de retorno (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 convierteen el valor de retorno de la llamada a la funci´n que comenz´ todo el proceso. o oHe aqu´ el aspecto que tiene el diagrama de pila para esta secuencia de llamadas ıa funci´n: o __main__ 6 factorial n 3 recurse 2 return 6 2 factorial n 2 recurse 1 return 2 1 factorial n 1 recurse 1 return 1 1 factorial n 0Los valores de retorno se muestran seg´n se pasan hacia la parte superior de ula pila. En cada marco, el valor de retorno es el valor de resultado, que es elproducto de n por recursivo.N´tese que en el ultimo marco las variables locales recursivo y resultado no o ´existen porque la rama que las crea no se ejecuta.5.6. Acto de feSeguir el flujo de ejecuci´n es una de las maneras de leer programas; pero puede ovolverse r´pidamente una tarea laber´ a ınitca. La alternativa es lo que llamamosel “acto de fe”. Cuando llegamos a una funci´n, en lugar de seguir el flujo de oejecuci´n, damos por sentado que la funci´n trabaja correctamente y devuelve o oel valor apropiado.De hecho, usted ya practica dicho salto de fe cuando usa funciones internas.Cuando llama a math.cos o a math.exp, no examina la implementaci´n de o
    • 56 Funciones productivasdichas funciones. Simplemente da por sentado que funcionan porque los queescribieron las bibliotecas internas de Python son buenos programadores.Lo mismo se aplica cuando llama a una de las funciones programadas por usted.Por ejemplo en la Secci´n 5.4, escribimos una funci´n llamada esDivisible o oque determina si un n´mero es divisible por otro. Una vez que nos hayamos uconvencido de que dicha funci´n es correcta, comprobando y examinando el oc´digo, podremos usar la funci´n sin tener siquiera que volver a mirar el c´digo o o ootra vez.Lo mismo vale para los programas recursivos. Cuando llegue a la llamada recur-siva, en lugar de seguir el flujo de ejecuci´n, tendr´ que dar por supuesto que o ıala llamada recursiva funciona (es decir, devuelve el resultado correcto) y luegopreguntarse: “suponiendo que puedo hallar el factorial de n − 1, ¿puedo hallarel factorial de n?” En este caso, est´ claro que s´ puede, multiplic´ndolo por n. a ı aPor supuesto, es un tanto extra˜o dar por supuesto que la funci´n est´ bien n o acuando ni siquiera ha acabado de escribirla, pero precisamente por eso se llamaacto de fe.5.7. Un ejemplo m´s aEn el ejemplo anterior, usamos variables temporales para ir apuntando los re-sultados y para hacer que el c´digo fuese m´s f´cil de depurar, pero podr´ o a a ıamoshabernos ahorrado unas cuantas l´ ıneas: def factorial(n): if n == 0: return 1 else: return n * factorial(n-1)De ahora en adelante, tenderemos a usar la versi´n m´s concisa, pero le reco- o amendamos que utilice la versi´n m´s expl´ o a ıcita mientras se halle desarrollandoc´digo. Cuando lo tenga funcionando, lo podr´ acortar, si se siente inspirado. o aDespu´s de factorial, el ejemplo m´s com´n de una funci´n matem´tica re- e a u o acursivamente definida es fibonacci, que presenta la siguiente definici´n: o f ibonacci(0) = 1 f ibonacci(1) = 1 f ibonacci(n) = f ibonacci(n − 1) + f ibonacci(n − 2);Traducido a Python, es como sigue:
    • 5.8 Comprobaci´n de tipos o 57 def fibonacci (n): if n == 0 or n == 1: return 1 else: return fibonacci(n-1) + fibonacci(n-2)Si intenta seguir el flujo de ejecuci´n aqu´ incluso para valores relativamente o ı,peque˜os de n, le puede dar un dolor de cabeza. Pero si confiamos en el acto de nfe, si da por supuesto que las dos llamadas recursivas funcionan correctamente,entonces estar´ claro que obtiene el resultado correcto al sumarlas juntas. a5.8. Comprobaci´n de tipos o¿Qu´ sucede si llamamos a factorial y le damos 1.5 como argumento? e >>> factorial (1.5) RuntimeError: Maximum recursion depth exceededTiene todo el aspecto de una recursi´n infinita. Pero, ¿c´mo ha podido ocurrir? o oHay una condici´n de salida o caso base: cuando n == 0. El problema es que el ovalor de n yerra el caso base.En la primera llamada recursiva, el valor de n es 0.5. En la siguiente vez su valores -0.5. A partir de ah´ se vuelve m´s y m´s peque˜o, pero nunca ser´ 0. ı, a a n aTenemos dos opciones. Podemos intentar generalizar la funci´n factorial para oque trabaje con n´meros de coma flotante, o podemos hacer que factorial ucompruebe el tipo de su par´metro. La primera opci´n se llama funci´n gamma, a o oy est´ m´s all´ del objetivo de este libro. As´ pues, tomemos la segunda. a a a ıPodemos usar la funci´n type para comparar el tipo del par´metro con el tipo o ade un valor entero conocido (por ejemplo 1). Ya que estamos en ello, podemosasegurarnos de que el par´metro sea positivo: a def factorial (n): if type(n) != type(1): print "El factorial est´ definido s´lo para enteros." a o return -1 elif n < 0: print "El factorial est´ definido s´lo para enteros a o positivos." return -1 elif n == 0: return 1 else: return n * factorial(n-1)
    • 58 Funciones productivasAhora tenemos tres condiciones de salida o casos base. El primero filtra losn´meros no enteros. El segundo evita los enteros negativos. En ambos casos, se umuestra un mensaje de error y se devuelve un valor especial, -1, para indicar aquien hizo la llamada a la funci´n que algo fue mal: o >>> factorial (1.5) El factorial esta definido solo para enteros. -1 >>> factorial (-2) El factorial esta definido solo para enteros positivos. -1 >>> factorial ("paco") El factorial esta definido solo para enteros. -1Si pasamos ambas comprobaciones, entonces sabemos que n es un entero positivoy podemos probar que la recursi´n termina. oEste programa muestra un patr´n que se llama a veces guardi´n. Las primeras o ados condicionales act´an como guardianes, protegiendo al c´digo que sigue de u olos valores que pudieran causar errores. Los guardianes hacen posible demostrarla correcci´n del c´digo. o o5.9. Glosariofunci´n productiva: Funci´n que devuelve un valor de retorno. o ovalor de retorno: El valor obtenido como resultado de una llamada a una funci´n. ovariable temporal: Variable utilizada para almacenar un valor intermedio en un c´lculo complejo. ac´digo muerto: Parte de un programa que no podr´ ejecutarse nunca, a me- o a nudo debido a que aparece tras una sentencia de return.None: Valor especial de Python que devuelven funciones que o bien no tienen sentencia de return o bien tienen una sentencia de return sin argumento.desarrollo incremental: Un m´todo de desarrollo de programas que busca e evitar el depurado a˜adiendo y probando una peque˜a cantidad de c´digo n n o en cada paso.andamiaje: El c´digo que se usa durante el desarrollo del programa pero que o no es parte de la versi´n final. o
    • 5.9 Glosario 59guardi´n: Una condici´n que comprueba y maneja circunstancias que pudieran a o provocar un error.
    • Cap´ ıtulo 6Iteraci´n o6.1. Asignaci´n m´ ltiple o uEs posible que haya descubierto que es posible hacer m´s de una asignaci´n a a ouna misma variable. El efecto de la nueva asignaci´n es redirigir la variable de omanera que deje de remitir al valor antiguo y empieze a remitir al valor nuevo. bruno = 5 print bruno, bruno = 7 print brunoLa salida del programa es 5 7, ya que la primera vez que imprimimos brunosu valor es 5, y la segunda vez su valor es 7. La coma al final de la primerasentencia print impide que se imprima una nueva l´ ınea en ese punto, por esoambos valores aparecen en la misma l´ınea.He aqu´ el aspecto de una asignaci´n m´ltiple en un diagrama de estado: ı o u 5 bruce 7Cuando hay asignaciones m´ltiples a una variable, es especialmente importante udistinguir entre una sentencia de asignaci´n y una sentencia de igualdad. Puesto oque Python usa el s´ ımbolo = para la asignaci´n, es tentador interpretar una osentencia como a = b como sentencia de igualdad. Pero no lo es.
    • 62 Iteraci´n oPara empezar, la igualdad es commutativa, y la asignaci´n no lo es. Por ejemplo oen matem´ticas si a = 7 entonces 7 = a. Pero en Python la sentencia a = 7 es alegal, y 7 = a no lo es.Y lo que es m´s, en matem´ticas, una sentencia de igualdad es verdadera todo a ael tiempo. Si a = b ahora, entonces a siempre ser´ igual a b. En Python, una asentencia de asignaci´n puede hacer que dos variables sean iguales, pero no otienen por qu´ quedarse as´ e ı. a = 5 b = a # a y b son ahora iguales a = 3 # a y b ya no son igualesLa tercera l´ ınea cambia el valor de a pero no cambia el valor de b, y por lo tantoya dejan de ser iguales. En algunos lenguajes de programaci´n, se usa para la oasignaci´n un s´ o ımbolo distinto, como <- o como :=, para evitar la confusi´n.oAunque la asignaci´n m´ltiple es util a menudo, debe usarla con cuidado. Si los o u ´valores de las variables van cambiando constantemente en distintas partes delprograma, podr´ suceder que el c´digo sea dif´ de leer y mantener. ıa o ıcil6.2. La sentencia whileUna de las tareas para las que los computadores se usan con frecuencia es laautomatizaci´n de tareas repetitivas. Repetir tareas similares o id´nticas es algo o eque los computadores hacen bien y las personas no hacen tan bien.Hemos visto dos programas, nLineas y cuenta atras, que usan la recursividadpara llevar a cabo la repetici´n, que tambi´n se llama iteraci´n. Por ser la o e oiteraci´n tan habitual, Python proporciona como lenguaje varias caracter´ o ısticasque la hacen m´s f´cil. La primera caracter´ a a ıstica que vamos a considerar es lasentencia while.´Este es el aspecto de cuenta atras con una sentencia while: def cuenta_atras(n): while n > 0: print n n = n-1 print "Despegando!"Como eliminamos la llamada recursiva, esta funci´n no es recursiva. oCasi podr´ leer una sentencia while como si fuera ingl´s (castellano “mien- ıa etras”). Quiere decir que “Mientras n sea mayor que cero, contin´a mostrando el uvalor de n y despu´s rest´ndole 1 al valor de n. Cuando llegues a cero, muestra e ala palabra “Despegando!”.
    • 6.2 La sentencia while 63M´s formalmente, el flujo de ejecuci´n de una sentencia while es el siguiente: a o 1. Evaluar la condici´n, devolviendo 0 o 1. o 2. Si la condici´n es falsa (0), salir de la sentencia while y continuar la o ejecuci´n en la siguiente sentencia. o 3. Si la condici´n es verdadera (1), ejecutar cada una de las sentencias en el o cuerpo del bucle while, y luego volver al paso 1.El cuerpo est´ formado por todas las sentencias bajo el encabezado que tienen ael mismo sangrado.Este tipo de flujo de llama bucle porque el tercer paso vuelve de nuevo arriba.N´tese que si la condici´n es falsa la primera vez que se atraviesa el bucle, las o osentencias del interior del bucle no se ejecutan nunca.El cuerpo del bucle debe cambiar el valor de una o m´s variables de manera que, allegado el momento, la condici´n sea falsa y el bucle termine. En caso contrario, oel bucle se repetir´ para siempre, que es lo que se llama bucle infinito. Una ainfinita fuente de diversi´n para los cient´ o ıficos inform´ticos es la observaci´n de a oque las instrucciones del champ´ “lavar, aclarar, repetir”, son un bucle infinito. uEn el caso de cuenta atras, podemos probar que el bucle terminar´ porque asabemos que el valor de n es finito, y podemos ver que el valor de n disminuyecada vez que se atraviesa el bucle (cada iteraci´n), de manera que ea la larga otenemos que llegar a cero. En otros casos no es tan f´cil decirlo: a def secuencia(n): while n != 1: print n, if n%2 == 0: # n es par n = n/2 else: # n es impar n = n*3+1La condici´n de este bucle es n != 1, de manera que el bucle continuar´ hasta o aque n sea 1, que har´ que la condici´n sea falsa. a oEn cada iteraci´n, el programa muestra como salida el valor de n y luego com- oprueba si es par o impar. Si es par, el valor de n se divide entre dos. Si es impar,el valor se sustituye por 3n+1. Por ejemplo, si el valor de comienzo (el argumentopasado a la secuencia) es 3, la secuencia resultante es 3, 10, 5, 16, 8, 4, 2, 1.Puesto que n a veces aumenta y a veces disminuye, no hay una prueba obvia deque n alcance alguna vez el valor 1, o de que el programa vaya a terminar. Paraalgunos valores particulares de n, podemos probar la terminaci´n. Por ejemplo, o
    • 64 Iteraci´n osi el valor de inicio es una potencia de dos, entonces el valor de n ser´ par cada avez que se pasa a trav´s del bucle, hasta que lleguemos a 1. El ejemplo anterior eacaba con dicha secuencia, empezando por 16.Dejando aparte valores particulares, la pregunta interesante es si podemos pro-bar que este programa terminar´ para todos los valores de n. Hasta la fecha, anadie ha sido capaz de probarlo o negarlo. Como actividad, reescriba la funci´n nLines de la secci´n 4.9 utili- o o zando iteraci´n en lugar de recursividad. o6.3. TablasUna de las cosas para las que resultan buenos los bucles es para generar datos ta-bulares. Antes de que los computadores estuvieran disponibles de forma masiva,la gente ten´ que calcular a mano logaritmos, senos, cosenos y otras funciones ıamatem´ticas. Para facilitarlo, los libros de matem´ticas conten´ largas tablas a a ınadonde aparec´ los valores de estas funciones. Confeccionar estas tablas era ıanuna tarea lenta y pesada, y el resultado estaba lleno de erratas.Cuando los computadores aparecieron en escena, una de las primeras reaccionesfue “¡Qu´ bueno! Podemos usar los computadores para generar las tablas, as´ no e ıhabr´ errores”. Result´ cierto (casi), pero no se vio m´s all´. Poco despu´s los a o a a ecomputadores y las calculadoras cient´ıficas se hicieron tan ubicuas que las tablasresultaron obsoletas.Bueno, casi. Resulta que para ciertas operaciones, los computadores usan tablaspara obtener un valor aproximado, y luego ejecutan c´lculos para mejorar la aaproximaci´n. En algunos casos, ha habido errores en las tablas subyacentes; el om´s famoso estaba en la tabla que el Pentium de Intel usaba para llevar a cabo ala divisi´n de coma flotante. oAunque una tabla logar´ıtmica ya no es tan util como lo fuera anta˜o, todav´ ´ n ıaconstituye un buen ejemplo de iteraci´n. El siguiente programa muestra una osecuencia de valores en la columna izquierda y sus logaritmos en la columnaderecha: x = 1.0 while x < 10.0: print x, ’t’, math.log(x) x = x + 1.0El t representa un car´cter de tabulaci´n. a o
    • 6.3 Tablas 65Tal como se van mostrando en la pantalla caracteres y cadenas, un se˜alador ninvisible llamado cursor lleva la cuenta de d´nde ir´ el pr´ximo car´cter. Tras o a o auna sentencia print, el cursor va normalmente al principio de la l´ ınea siguiente.El car´cter de tabulaci´n hace que el cursor se desplace a la derecha hasta que a oalcance uno de los marcadores de tabulaci´n. Los tabuladores son utiles para o ´alinear columnas de texto, como en la salida del programa anterior: 1.0 0.0 2.0 0.69314718056 3.0 1.09861228867 4.0 1.38629436112 5.0 1.60943791243 6.0 1.79175946923 7.0 1.94591014906 8.0 2.07944154168 9.0 2.19722457734Si estos valores le parecen raros, recuerde que la funci´n log usa como base e. oDebido a que las potencias de dos son muy importantes en las ciencias de lacomputaci´n, generalmente querremos hallar los logaritmos en relaci´n con la o obase dos. Para llevarlo a cabo, podemos usar la siguiente f´rmula: o loge x log2 x = (6.1) loge 2Cambiar la sentencia de salida a: print x, ’t’, math.log(x)/math.log(2.0)devuelve 1.0 0.0 2.0 1.0 3.0 1.58496250072 4.0 2.0 5.0 2.32192809489 6.0 2.58496250072 7.0 2.80735492206 8.0 3.0 9.0 3.16992500144Podemos ver que 1, 2, 4 y 8 son potencias de dos, porque sus logaritomos debase 2 son n´meros enteros. Si quisi´ramos encontrar los logaritmos de otras u epotencias de dos, podr´ ıamos modificar el programa de la siguiente manera: x = 1.0 while x < 100.0:
    • 66 Iteraci´n o print x, ’t’, math.log(x)/math.log(2.0) x = x * 2.0Ahora, en lugar de a˜adir algo a x cada vez que atravesamos el bucle, que ndevuelve una secuencia aritm´tica, multiplicamos x por algo, devolviendo una esecuencia geom´trica. El resultado es: e 1.0 0.0 2.0 1.0 4.0 2.0 8.0 3.0 16.0 4.0 32.0 5.0 64.0 6.0Debido a que usamos caracteres de tabulaci´n entre las columnas, la posici´n de o ola segunda columna no depende del n´mero de d´ u ıgitos de la primera columna.Las tablas logar´ ıtimicas quiz´s ya no sean utiles, pero conocer las potencias de a ´dos no ha dejado de serlo para los cient´ ıficos inform´ticos. a Como actividad, modifique el programa para que muestre las poten- cias de dos hasta 65536 (es decir, 216 ). Impr´ ımala y memor´ ıcela.El car´cter de barra invertida en ’t’ indica el principio de una secuencia de aescape. Las secuencias de escape se usan para representar caracteres invisiblescomo tabulaciones y retornos de carro. La secuencia n representa un retornode carro.Una sentencia de escape puede aparecer en cualquier lugar de una cadena; en elejemplo, la secuencia de escape del tabulador es la unica de la cadena. ´¿C´mo cree que puede representar una barra invertida en una cadena? o Como ejercicio, escriba un unica cadena que ´ presente esta salida.6.4. Tablas de dos dimensionesUna tabla de dos dimensiones es una tabla en la que Usted elige una fila yuna columna y lee el valor de la intersecci´n. Un buen ejemplo es una tabla de o
    • 6.5 Encapsulado y generalizaci´n o 67multiplicar. Supongamos que desea imprimir una tabla de multiplicar para losvalores del 1 al 6.Una buena manera de comenzar es escribir un bucle sencillo que imprima losm´ltiplos de 2, todos en una l´ u ınea. i = 1 while i <= 6: print 2*i, ’t’, i = i + 1 printLa primera l´ ınea inicializa una variable lllamada i, que actuar´ como contador, ao variable de bucle. Conforme se ejecuta el bucle, el valor de i se incrementade 1 a 6. Cuando i vale 7, el bucle termina. Cada vez que se atraviesa el bucle,imprimimos el valor 2*i seguido por tres espacios.De nuevo, la coma de la sentencia print suprime el salto de l´ınea. Despu´s de ecompletar el bucle, la segunda sentencia print crea una l´ ınea nueva.La salida de este programa es: 2 4 6 8 10 12Hasta ahora, bien. El siguiente paso es encapsular y generalizar.6.5. Encapsulado y generalizaci´n oPor “encapsulado” generalmente se entiende tomar una pieza de c´digo y envol- overla en una funci´n, permiti´ndole obtener las ventajas de todo aquello para o elo que valen las funciones. Hemos visto dos ejemplos de encapsulado, cuandoescribimos imprimeParidad en la Secci´n 4.5 y esDivisible en la Secci´n 5.4. o oPor “generalizaci´n” entendemos tomar algo espec´ o ıfico, como imprimir los m´lti- uplos de 2, y hacerlo m´s general, como imprimir los m´ltiplos de cualquier entero. a uHe aqu´ una funci´n que encapsula el bucle de la secci´n anterior y la generaliza ı o opara imprimir m´ltiplos de n. u def imprimeMultiplos(n): i = 1 while i <= 6: print n*i, ’t’, i = i + 1 print
    • 68 Iteraci´n oPara encapsular, todo lo que hubimos de hacer fue a˜adir la primera l´ n ınea, quedeclara el nombre de la funci´n y la lista de par´metros. Para generalizar, todo o alo que tuvimos que hacer fue sustituir el valor 2 por el par´metro n. aSi llamamos a esta funci´n con el argumento 2, obtenemos la misma salida que oantes. Con el argumento 3, la salida es: 3 6 9 12 15 18y con argumento 4, la salida es 4 8 12 16 20 24A estas alturas es probable que haya adivinado c´mo vamos a imprimir una otabla de multiplicaci´n: llamaremos a imprimeMultiplos repetidamente con odiferentes argumentos. De hecho, podemos a usar otro bucle: i = 1 while i <= 6: imprimeMultiplos(i) i = i + 1Observe hasta qu´ punto este bucle es similar al que hay en el interior de eimprimeMultiplos. Todo lo que hicimos fue sustituir la sentencia print poruna llamada a una funci´n. oLa salida de este programa es una tabla de multiplicaci´n: o 1 2 3 4 5 6 2 4 6 8 10 12 3 6 9 12 15 18 4 8 12 16 20 24 5 10 15 20 25 30 6 12 18 24 30 366.6. M´s encapsulaci´n a oPara dar m´s ejemplos de encapsulaci´n, tomaremos el c´digo del final de la a o oSecci´n 6.5 y lo envolveremos en una funci´n: o o def imprimeTablaMult(): i = 1 while i <= 6: imprimeMultiplos(i) i = i + 1El proceso que mostramos aqu´ es un plan de desarrollo habitual. Se desa- ırrolla gradualmente el c´digo a˜adiendo l´ o n ıneas fuera de cualquier funci´n o en o
    • 6.7 Variables locales 69el int´rprete. Cuando conseguimos que funcionen, se extraen y se envuelven en euna funci´n. oEste plan de desarrollo es especialmente si, al comenzar a escribir, no sabec´mo dividir el programa en funciones. Este enfoque le permite dise˜arlo sobre o nla marcha.6.7. Variables localesQuiz´ se est´ preguntando c´mo podemos usar la misma variable tanto en a e oimprimeMultiplos como en imprimeTablaMult. ¿No habr´ problemas cuan- ado una de las funciones cambie los valores de la variable?La respuesta es no, ya que la variable i en imprimeMultiplos y la variable iin imprimeTablaMult no son la misma variable.Las variables creadas dentro de una funci´n son locales. No puede acceder a una ovariable local fuera de su funci´n “hu´sped”. Eso significa que es posible tener o em´ltiples variables con el mismo nombre, siempre que no est´n en la misma u efunci´n. oEl diagrama de pila de esta funci´n muestra claramente que las dos variables ollamadas i no son la misma variable. Pueden referirse a diferentes valores, ycambiar uno no afecta al otro. printMultTable 1 i 2 3 printMultiples 1 n 3 i 2El valor de i en imprimeTablaMult va desde 1 hasta 6. En el diagrama, re-sulta ser 3. El pr´ximo recorrido del bucle ser´ 4. En cada recorrido del bucle, o aimprimeTablaMult llama a imprimeMultiplos con el valor actual de i comoargumento. Ese valor se asigna al par´metro n. aDentro de imprimeMultiplos, el valor de i va desde 1 hasta 6. En el diagrama,resulta ser 2. Los cambios en esta variable no tienen ning´n efecto sobre el valor ude i en imprimeTablaMult.Es habitual y perfectamente legal tener diferentes variables locales con el mismonombre. En especial, los nombres i, j y k se suelen usar como variables de
    • 70 Iteraci´n obucle. Si evita usarlas en una funci´n porque las utiliz´ en alg´n otro lugar, o o uprobablemente consiga que el programa sea m´s dif´ de leer. a ıcil6.8. M´s generalizaci´n a oComo otro ejemplo de generalizaci´n, imagine que desea un programa que im- oprima una tabla de multiplicaci´n de cualquier tama˜o, y no s´lo la tabla de o n o6x6. Podr´ a˜adir un par´metro a imprimeTablaMult: ıa n a def imprimeTablaMult(mayor): i = 1 while i <= mayor: imprimeMultiplos(i) i = i + 1Hemos sustituido el valor 6 con el par´metro mayor. Si ahora se llama a aimprimeTablaMult con el argumento 7, obtenemos: 1 2 3 4 5 6 2 4 6 8 10 12 3 6 9 12 15 18 4 8 12 16 20 24 5 10 15 20 25 30 6 12 18 24 30 36 7 14 21 28 35 42lo que es correcto, excepto por el hecho de que seguramente queremos que latabla est´ cuadrada, con el mismo n´mero de filas que de columnas. Para hacer- e ulo, a˜adimos otro par´metro a imprimeMultiplos, a fin de especificar cu´ntas n a acolumnas tendr´ que tener la tabla. ıaS´lo para fastidiar, llamaremos tambi´n a este par´metro mayor, para demostrar o e aque diferentes funciones pueden tener par´metros con el mismo nombre (al igual aque las variables locales). Aqu´ tenemos el programa completo: ı def imprimeMultiplos(n, mayor): int i = 1 while i <= mayor: print n*i, ’t’, i = i + 1 print def imprimeTablaMult(mayor): int i = 1 while i <= mayor:
    • 6.9 Funciones 71 imprimeMultiplos(i, mayor) i = i + 1N´tese que al a˜adir un nuevo par´metro, tuvimos que cambiar la primera l´ o n a ıneade la funci´n (el encabezado de la funci´n), y tuvimos tambi´n que cambiar el o o elugar donde se llama a la funci´n en imprimeTablaMult. oSeg´n lo esperado, este programa genera una tabla cuadrada de 7x7: u 1 2 3 4 5 6 7 2 4 6 8 10 12 14 3 6 9 12 15 18 21 4 8 12 16 20 24 28 5 10 15 20 25 30 35 6 12 18 24 30 36 42 7 14 21 28 35 42 49Cuando generaliza correctamente una funci´n, a menudo se encuentra con que oel programa resultante tiene capacidades que Usted no pensaba. Por ejemplo,quiz´ observe que la tabla de multiplicaci´n es sim´trica, porque ab = ba, de a o emanera que todas las entradas de la tabla aparecen dos veces. Puede ahorrartinta imprimiendo s´lo la mitad de la tabla. Para hacerlo, s´lo tiene que cambiar o ouna l´ınea de imprimeTablaMult. Cambie imprimeMultiplos(i, mayor)por imprimeMultiplos(i, i)y obtendr´ a 1 2 4 3 6 9 4 8 12 16 5 10 15 20 25 6 12 18 24 30 36 7 14 21 28 35 42 49 Como actividad, siga o trace la ejecuci´n de esta nueva versi´n de o o imprimeTablaMult para hacerse una idea de c´mo funciona. o6.9. FuncionesHasta el momento hemos mencionado en alguna ocasi´n “todas las cosas para olas que sirven las funciones”. Puede que ya se est´ preguntando qu´ cosas son e eexactamente. He aqu´ algunas de las razones por las que las funciones son utiles: ı ´
    • 72 Iteraci´n o Al dar un nombre a una secuencia de sentencias, hace que su programa sea m´s f´cil de leer y depurar. a a Dividir un programa largo en funciones le permite separar partes del pro- grama, depurarlas aisladamente, y luego recomponerlas en un todo. Las funciones facilitan tanto la recursividad como la iteraci´n. o Las funciones bien dise˜adas son generalmente utiles para m´s de un pro- n ´ a grama. Una vez escritas y depuradas, puden reutilizarse.6.10. Glosarioasignaci´n m´ ltiple: Hacer m´s de una asignaci´n a la misma variable du- o u a o rante la ejecuci´n de un programa. oiteraci´n: La ejecuci´n repetida de un conjunto de sentencias por medio de o o una llamada recursiva a una funci´n o un bucle. obucle: Sentencia o grupo de sentencias que se ejecutan repetidamente hasta que se cumple una condici´n de terminaci´n. o obucle infinito: Bucle cuya condici´n de terminaci´n nunca se cumple. o ocuerpo: Las sentencias que hay dentro de un bucle.variable de bucle: Variable que se usa para determinar la condici´n de ter- o minaci´n de un bucle. otabulador: Car´cter especial que hace que el cursor se mueva hasta la siguiente a marca de tabulaci´n en la l´ o ınea actual.nueva l´ ınea: Un car´cter especial que hace que le cursor se mueva al inicio de a la siguiente l´ ınea.cursor: Un marcador invisible que sigue el rastro de d´nde se imprimir´ el o a siguiente car´cter. asecuencia de escape: Car´cter de escape () seguido por uno o m´s caracteres a a imprimibles, que se usan para designar un car´cter no imprimible. aencapsular: Dividir un programa largo y complejo en componentes (como las funciones) y aislar los componentes unos de otros (por ejemplo usando variables locales).
    • 6.10 Glosario 73generalizar: Sustituir algo innecesariamente espec´ıfico (como es un valor cons- tante) con algo convenientemente general (como es una variable o par´me- a tro). La generalizaci´n hace el c´digo m´s vers´til, m´s apto para reutili- o o a a a zarse y algunas veces incluso m´s f´cil de escribir. a aplan de desarrollo: Proceso para desarrollar un programa. En este cap´ ıtulo, hemos mostrado un estilo de desarrollo basado en desarrollar c´digo para o hacer cosas simples y espec´ ıficas, y luego encapsularlas y generalizarlas.
    • Cap´ ıtulo 7Cadenas7.1. Un tipo de datos compuestoHasta el momento hemos visto tres tipos: int, float, y string. Las cadenasson cuantitativamente diferentes de los otros dos porque est´n hechas de piezas amenores: caracteres.Los tipos que comprenden piezas menores se llaman tipos de datos com-puestos. Dependiendo de qu´ estemos haciendo, podemos querer tratar un tipo ecompuesto como una unica cosa o acceder a sus partes. Esta ambig¨edad es util. ´ u ´El operador corchete selecciona un car´cter suelto de una cadena. a>>> fruta = "banana">>> letra = fruta[1]>>> print letraLa expresi´n fruta[1] selecciona el car´cter n´mero 1 de fruta. La variable o a uletra apunta al resultado. Cuando mostramos letra, nos encontramos con unasorpresa:aLa primera letra de "banana" no es a. A no ser que usted sea un programador.Por perversas razones, los cient´ıficos de la computaci´n siempre empiezan a ocontar desde cero. La 0-sima letra (“cer´sima”) de "banana" es b. La 1-´sima o e(“un´sima”) es a, y la 2-´sima (“dos´sima”) letra es n. e e eSi quiera la cer´sima letra de una cadena, simplemente pone 0, o cualquier oexpresi´n de valor 0, entre los corchetes: o
    • 76 Cadenas>>> letra = fruta[0]>>> print letrabA la expresi´n entre corchetes se le llama ´ o ındice. Un ´ ındice identifica a unmiembro de un conjunto ordenado, en este caso el conjunto de caracteres de lacadena. El ´ ındice indica cu´l quiere usted, de ah´ el nombre. Puede ser cualquier a ıexpresi´n entera. o7.2. LongitudLa funci´n len devuelve el n´mero de caracteres de una cadena: o u>>> fruta = "banana">>> len(fruta)6Para obtener la ultima letra de una cadena puede sentirse tentado a probar algo ´como esto:longitud = len(fruta)ultima = fruta[longitud] # ERROR!Eso no funcionar´. Provoca un error en tiempo de ejecuci´n IndexError: a ostring index out of range. La raz´n es que no hay una sexta letra en o"banana". Como empezamos a contar por cero, las seis letras est´n numeradas adel 0 al 5. Para obtener el ultimo car´cter tenemos que restar 1 de longitud: ´ alongitud = len(fruta)ultima = fruta[longitud-1]De forma alternativa, podemos usar ´ ındices negativos, que cuentan hacia atr´s adesde el final de la cadena. La expresi´n fruta[-1] nos da la ultima letra. o ´fruta[-2] nos da la pen´ltima, y as´ u ı.7.3. Recorrido y el bucle forMuchos c´lculos incluyen el proceso de una cadena car´cter a car´cter. A me- a a anudo empiezan por el principio, seleccionan cada car´cter por turno, hacen algo acon ´l y siguen hasta el final. Este patr´n de proceso se llama recorrido. Una e oforma de codificar un recorrido es una sentencia while:indice = 0while indice < len(fruta): letra = fruta[indice]
    • 7.3 Recorrido y el bucle for 77 print letra indice = indice + 1Este bucle recorre la cadena y muestra cada letra en una l´ ınea distinta. Lacondici´n del bucle es indice < len(fruta), de modo que cuando indice es oigual a la longitud de la cadena, la condici´n es falsa y no se ejecuta el cuerpo del obucle. El ultimo car´cter al que se accede es el que tiene el ´ ´ a ındice len(fruta)-1,que es el ultimo car´cter de la cadena. ´ a Como ejercicio, escriba una funci´n que tome una cadena como ar- o gumento y entregue las letras en orden inverso, una por l´ ınea.Es tan habitual usar un ´ ındice para recorrer un conjunto de valores que Pythonfacilita una sintaxis alternativa m´s simple: el bucle for: afor car in fruta: print carCada vez que recorremos el bucle, se asigna a la variable car el siguiente car´cter ade la cadena. El bucle contin´a hasta que no quedan caracteres. uEl ejemplo siguiente muestra c´mo usar la concatenaci´n junto a un bucle for o opara generar una serie abeced´rica. “Abeced´rica” es la serie o lista en la que a acada uno de los elementos aparece en orden alfab´tico. Por ejemplo, en el libro ede Robert McCloskey Make Way for Ducklings (Dejad paso a los patitos), losnombres de los patitos son Jack, Kack, Lack, Mack, Nack, Ouack, Pack, y Quack.Este bucle saca esos nombres en orden:prefijos = "JKLMNOPQ"sufijo = "ack"for letra in prefijos: print letra + sufijoLa salida del programa es:JackKackLackMackNackOackPackQackPor supuesto, esto no es del todo correcto, porque “Ouack” y “Quack” no est´n acorrectamente escritos.
    • 78 Cadenas Como ejercicio, modifique el programa para corregir este error.7.4. Porciones de cadenasLlamamos porci´n a un segmento de una cadena. La selecci´n de una porci´n o o oes similar a la selecci´n de un car´cter: o a>>> s = "Pedro, Pablo, y Mar´a" ı>>> print s[0:5]Pedro>>> print s[7:12]Pablo>>> print s[15:20]Mar´a ıEl operador [n:m] devuelve la parte de la cadena desde el en´simo car´cter e ahasta el “em´simo”, incluyendo el primero pero excluyendo el ultimo. Este com- e ´portamiento contradice a nuestra intuici´n; tiene m´s sentido si imagina los o a´ındices se˜alando entre los caracteres, como en el siguiente diagrama: n fruta "banana" indice 0 1 2 3 4 5 6Si omite el primer ´ ındice (antes de los dos puntos), la porci´n comienza al oprincipio de la cadena. Si omite el segundo ´ ındice, la porci´n llega al final de la ocadena. As´ı:>>> fruta = "banana">>> fruta[:3]’ban’>>> fruta[3:]’ana’¿Qu´ cree usted que significa s[:]? e7.5. Comparaci´n de cadenas oLos operadores de comparaci´n trabajan sobre cadenas. Para ver si dos cadenas oson iguales:
    • 7.6 Las cadenas son inmutables 79if palabra == "banana": print "S´, no tenemos bananas!" ıOtras operaciones de comparaci´n son utiles para poner palabras en orden al- o ´fab´tico: eif palabra < "banana": print "Tu palabra," + palabra + ", va antes de banana."elif palabra > "banana": print "Tu palabra," + palabra + ", va despu´s de banana." eelse: print "S´, no tenemos bananas!" ıSin embargo, deber´ usted ser consciente de que Python no maneja las may´scu- ıa ulas y min´sculas como lo hace la gente. Todas las may´suculas van antes de la u umin´sculas. Como resultado de ello: uTu palabra, Zapato, va antes de banana.Una forma com´n de abordar este problema es convertir las cadenas a un forma- uto est´ndar, como pueden ser las min´sculas, antes de realizar la comparaci´n. a u oUn problema mayor es hacer que el programa se d´ cuenta de que los zapatos eno son frutas.7.6. Las cadenas son inmutablesEs tentador usar el operador [] en el lado izquierdo de una asignaci´n, con la ointenci´n de cambiar un car´cter en una cadena. Por ejemplo: o asaludo = "Hola, mundo"saludo[0] = ’M’ # ERROR!print saludoEn lugar de presentar la salida Mola, mundo, este c´digo presenta el siguien- ote error en tiempo de ejecuci´n TypeError: object doesn’t support item oassignment.Las cadenas son inmutables, lo que significa que no puede cambiar una cade-na existente. Lo m´s que puede hacer es crear una nueva cadena que sea una avariaci´n de la original: osaludo = "Hola, mundo"nuevoSaludo = ’M’ + saludo[1:]print nuevoSaludo
    • 80 CadenasAqu´ la soluci´n es concatenar una nueva primera letra a una porci´n de saludo. ı o oEsta operaci´n no tiene efectos sobre la cadena original. o7.7. Una funci´n “encuentra” o¿Qu´ hace la siguiente funci´n? e odef encuentra(cad, c): indice = 0 while indice < len(cad): if cad[indice] == c: return indice indice = indice + 1 return -1En cierto sentido, encuentra es lo contrario del operador []. En lugar de tomarun ´ındice y extraer el car´cter correspondiente, toma un car´cter y encuentra a ael ´ ındice donde aparece el car´cter. Si el car´cter no se encuentra, la funci´n a a odevuelve -1.Este es el primer ejemplo que hemos visto de una sentencia return dentro deun bucle. Si cad[indice] == c, la funci´n vuelve inmediatamente, escapando odel bucle prematuramente.Si el car´cter no aparece en la cadena, el programa sale del bucle normalmente ay devuelve -1.Este patr´n de computaci´n se llama a veces un recorrido “eureka” porque en o ocuanto encontramos lo que buscamos, podemos gritar “¡Eureka!” y dejar debuscar. A modo de ejercicio, modifique la funci´n encuentra para que acepte o un tercer par´metro, el ´ a ındice de la cadena donde deber´ empezar a ıa buscar.7.8. Bucles y conteoEl programa que sigue cuenta el n´mero de veces que la letra a aparece en una ucadena:
    • 7.9 El m´dulo “string” o 81fruta = "banana"cuenta = 0for car in fruta: if car == ’a’: cuenta = cuenta + 1print cuentaEste programa muestra otro patr´n de computaci´n llamado contador. La o ovariable cuenta se incializa a 0 y luego se incrementa cada vez que se encuentrauna a. (Incrementar es aumentar en uno; es lo contario de decrementar, ysin relaci´n alguna con “excremento”, que es un nombre.) Al salir del bucle, ocuenta contiene el resultado – el n´mero total de aes. u Como ejercicio, encapsule este c´digo en una funci´n llamada o o cuentaLetras, y general´ ıcela de forma que acepte la cadena y la letra como par´metros. a Como un segundo ejercicio, reescriba esta funci´n para que en lu- o gar de recorrer la cadena, use la versi´n de tres par´metros de o a encuentra del anterior.7.9. El m´dulo “string” oEl m´dulo string contiene funciones utiles para manipular cadenas. Como es o ´habitual, tenemos que importar el m´dulo antes de poder usarlo: o>>> import stringEl m´dulo string incluye una funci´n llamada find que hace lo mismo que la o ofunci´n encuentra que escribimos. Para llamarla debemos especificar el nombre odel m´dulo y el nombre de la funci´n por medio de la notaci´n de punto. o o o>>> fruta = "banana">>> indice = string.find(fruta, "a")>>> print indice1Este ejemplo demuestra uno de los beneficios de los m´dulos: ayudan a evitar olas colisiones entre los nombres de las funciones predefinidas y las definidas porel usuario. Al usar la notaci´n de punto podr´ o ıamos especificar qu´ versi´n de e ofind queremos en caso de haberle daddo un nombre en ingl´s a nuestra funci´n. e oEn realidad, string.find es m´s general que nuestra versi´n. Para empezar, a opuede encontrar subcadenas, no s´lo caracteres: o
    • 82 Cadenas>>> string.find("banana", "na")2Adem´s, acepta un argumento adicional que especifica el ´ a ındice en el que deber´ ıacomenzar:>>> string.find("banana", "na", 3)4O puede tomar dos argumentos adicionales que especifican un intervalo de ´ ındi-ces:>>> string.find("sus", "s", 1, 2)-1En este ejemplo, la b´squeda falla porque la letra s no aparece en el intervalo ude ´ ındices desde 1 hasta 2 (sin incluir 2).7.10. Clasificaci´n de caracteres oA menudo viene bien examinar un car´cter y comprobar si es una letra may´scu- a ula o min´scula, o si es un car´cter o un d´ u a ıgito. El m´dulo string proporciona ovarias constantes que son utiles para estos menesteres. ´La cadena string.lowercase contiene todas las letras que el sistema conside-ra como min´sculas. De forma similar, string.uppercase contiene todas las umay´sculas. Pruebe lo que sigue y vea qu´ obtiene: u e>>> print string.lowercase>>> print string.uppercase>>> print string.digitsPodemos usar estas constantes y find para clasificar caracteres. Por ejemplo,si find(lowercase, c) devuelve un valor que no sea -1, entonces c es unamin´scula: udef esMinuscula(c): return find(string.lowercase, c) != -1Alternativamente, podemos aprovecharnos del operador in, que determina si uncar´cter aparece en una cadena: adef esMinuscula(c): return c in string.lowercase
    • 7.11 Glosario 83Como una alternativa m´s, podemos usar el operador de comparaci´n, aunque a oesta soluci´n s´lo sea pr´ctica para el alfabeto ingl´s: o o a edef esMinuscula(c): return ’a’ <= c <= ’z’Si c est´ entre a y z, tiene que ser una min´scula. a u Como ejercicio, explique qu´ versi´n de esMinuscula cree que es e o m´s r´pida. ¿Puede pensar en otras razones aparte de la velocidad a a para preferir una sobre la otra?Otra constante definida en el m´dulo string puede sorprenderle cuando la oimprima:>>> print string.whitespaceLos caracteres de whitespace mueven el cursor sin imprimir nada. Crean losespacios en blanco entre los caracteres visibles (al menos sobre papel blanco).La constante string.whitespace contiene todos los caracteres de espacio enblanco, inclu´ ıdos espacio, tabulador (t), y salto de l´ ınea (n).Hay otras funciones utiles en el m´dulo string, pero este libro no pretende ser ´ oun manual de referencia. Por otra parte, la Referencia de la Biblioteca de Pythons´ lo es. Junto con un mont´n m´s de documentaci´n, est´ disponible en el sitio ı o a o aweb de Python, www.python.org.7.11. Glosariotipo de datos compuesto: Un tipo de datos en el que los valores est´n hechos a de componentes o elementos que son a su vez valores.recorrer: Realizar de forma iterativa una operaci´n similar sobre cada uno de o los elementos de un conjunto.´ındice: Una variable o valor usado para seleccionar un miembro de un conjunto ordenado, como puede ser un car´cter de una cadena. aporci´n: Una parte de una cadena especificada por un intervalo de ´ o ındices.mutable: Un tipo de datos compuesto a cuyos elementos se les puede asignar nuevos valores.contador: Una variable usada para contar algo, normalmente inicializado a cero e incrementado posteriormente.
    • 84 Cadenasincrementar: Aumentar el valor de una variable en una unidad.decrementar: Disminuir el valor de una variable en una unidad.espacio en blanco: Cualquiera de los caracteres que mueven el cursor sin im- primir caracteres visibles. La constante string.whitespace contiene to- dos los caracterse de espacio en blanco.
    • Cap´ ıtulo 8ListasUna lista es un conjunto ordenado de valores, en el cual cada valor va identifica-do por un ´ ındice. Los valores que constituyen una lista son sus elementos. Laslistas son similares a las cadenas de texto (strings), que son conjuntos ordenadosde caracteres, excepto en que los elementos de una lista pueden ser de cualquiertipo. Las listas y las cadenas, y otras cosas que se comportan como conjuntosordenados, se llaman secuencias.8.1. Valores de una listaHay varias maneras de crear una nueva lista; la m´s sencilla es encerrar sus aelementos entre corchetes: [10, 20, 30, 40] ["spam", "el´stico", "golondrina"] aEl primer ejemplo es una lista de cuatro enteros. El segundo es una lista de trescadenas de texto. Los elementos de una lista no tienen por qu´ ser del mismo etipo. La siguiente lista contiene una cadena, un n´mero con decimales y un uentero, y, maravilla de las maravillas, otra lista: ["hola", 2.0, 5, [10, 20]]Se dice que una lista dentro de otra lista est´ anidada. aLas listas que contienen n´meros enteros consecutivos son comunes, de manera uque Python proporciona una manera sencilla de crearlas: >>> range(1,5) [1, 2, 3, 4]
    • 86 ListasLa funci´n range toma dos argumentos y devuelve una lista que contiene todos olos enteros entre el primero y el segundo, ¡incluyendo el primero pero no elsegundo!Hay dos formas alternativas para range. Con un solo argumento, crea una listaque empieza desde 0: >>> range(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]Si hay un tercer argumento, especificar´ el espacio entre dos valores sucesivos; aa esto se le llama paso (step). Este ejemplo cuenta de 1 a 10 de dos en dos (conpasos de 2). >>> range(1, 10, 2) [1, 3, 5, 7, 9]Para terminar, hay una lista especial que no contiene elementos. Se la llamalista vac´ y se representa []. ıaCon todas estas maneras para crear listas, ser´ decepcionante que no pudi´ra- ıa emos asignar valores de listas a variables o pasar listas como par´metros a fun- aciones. Por supuesto que podemos.vocabulario = ["mejorar", "castigar", "defenestrar"]numeros = [17, 123]vacio = []print vocabulario, numeros, vacio[’mejorar’, ’castigar’, ’defenestrar’] [17, 123] []8.2. Acceso a los elementosLa sintaxis para acceder a los elementos de una lista es la misma que paraacceder a los caracteres de una cadena: el operador corchetes []. La expresi´n odentro de los corchetes especifica el ´ ındice. Recuerde que los ´ındices siemprecomienzan en cero: print numeros[0] numeros[1] = 5El operador [] puede aparecer en cualquier parte de una expresi´n. Cuando oaparece a la izquierda de una asignaci´n, cambia uno de los elementos de la olista, de manera que el “un´simo” elemento de numeros, que era 123, ahora es e5.Se puede usar como ´ ındice cualquier expresi´n entera. o
    • 8.3 Longitud (tama˜ o) de una lista n 87>>> numeros[3-2]5>>> numeros[1.0]TypeError: sequence index must be integerSi intenta acceder (leer o modificar) un elemento que no existe, obtendr´ un aerror en tiempo de ejecuci´n: o>>> numeros[2] = 5IndexError: list assignment index out of rangeSi se da un ´ ındice negativo, se cuenta hacia atr´s desde el final de la lista. a>>> numeros[-1]5>>> numeros[-2]17>>> numeros[-3]IndexError: list index out of rangenumeros[-1] es el ultimo elemento de la lista, numeros[-2] es el pen´ltimo, y ´ unumeros[-3] no existe.Es muy habitual usar una varible de bucle como ´ ındice para una lista: jinetes = ["guerra", "hambre", "peste", "muerte"] i = 0 while i < 4: print jinetes[i] i = i + 1Este bucle while cuenta desde 0 hasta 4. Cuando la variable de bucle vale 4, lacondici´n falla y acaba el bucle. Por tanto, el cuerpo del bucle s´lo se ejecuta o ocuando i es 0, 1, 2 y 3.Cada vez que recorremos el bucle, la variable i se usa como ´ ındice de la lis-ta, imprimiendo el elemento i-´simo. Esta plantilla de computaci´n se llama e orecorrido de lista.8.3. Longitud (tama˜ o) de una lista nLa funci´n len toma una lista y devuelve su tama˜o. Es una buena idea usar o neste valor como l´ ımite superior de un bucle, en lugar de una constante. De estamanera, si el tama˜o de la lista cambia, no habr´ que estar haciendo cambios n aen todos los bucles; funcionar´n correctamente con cualquier tama˜o de lista. a n jinetes = ["guerra", "hambre", "peste", "muerte"] i = 0
    • 88 Listas while i < len(jinetes): print jinetes[i] i = i + 1La ultima vez que se ejecuta el cuerpo del bucle, i es len(jinetes) - 1, que es ´el ´ ındice del ultimo elemento. Cuando i se iguala a len(jinetes), la condici´n ´ ofalla y no se ejecuta el cuerpo, lo que es una cosa buena, ya que len(jinetes)no es un ´ındice legal.Aunque una lista puede contener otra lista como elemento, la lista anidadacuenta como un elemento sencillo. El tama˜o de esta lista es 4: n [’spam!’, 1, [’Brie’, ’Roquefort’, ’Pol le Veq’], [1, 2, 3]] Como ejercicio, escriba un bucle que recorra la lista anterior e im- prima la longitud de cada elemento. ¿qu´ ocurre si env´ un entero e ıa a len?8.4. Pertenencia a una listain es un operador booleano que comprueba la pertenencia a una secuencia. Lousamos en la Secci´n 7.10 con las cadenas, pero tambi´n funciona con las listas o ey otras secuencias:>>> jinetes = [’guerra’, ’hambre’, ’peste’, ’muerte’]>>> ’peste’ in jinetes1>>> ’libertinaje’ in jinetes0Como “peste” es un miembro de la lista jinetes, el operador in devuelveverdadero. Como “libertinaje” no est´ en la lista, in devuelve falso. aPodemos usar not en combinaci´n con in para comprobar si un elemento no es omiembro de una lista:>>> ’libertinaje’ not in jinetes18.5. Listas y bucles forEl bucle for que vimos en la Secci´n 7.3 tambi´n funciona con las listas. La o esintaxis generalizada de un bucle for es:
    • 8.6 Operaciones con listas 89 for VARIABLE in LISTA: CUERPOEsta sentencia es equivalente a: i = 0 while i < len(LISTA): VARIABLE = LISTA[i] CUERPO i = i + 1El bucle for es m´s conciso porque podemos eliminar la variable de bucle, i. aAqu´ tenemos el bucle anterior con un bucle for: ı for jinete in jinetes: print jineteM´s a´n, casi se lee igual que en espa˜ol, “Para (cada) jinete en (la lista de) a u njinetes, imprime (el nombre del) jinete”.Se puede usar cualquier expresi´n de lista en un bucle for: o for numero in range(20): if numero % 2 == 0: print numero for fruta in ["pl´tano", "manzana", "membrillo"]: a print "Me gusta comer " + fruta + "s!"El primer ejemplo imprime todos los n´meros pares entre el 0 y el 19. El segundo uejemplo expresa su entusiasmo por diferentes frutas.8.6. Operaciones con listasEl operador + concatena listas: >>> a = [1, 2, 3] >>> b = [4, 5, 6] >>> c = a + b >>> print c [1, 2, 3, 4, 5, 6]De forma similar, el operador * repite una lista un n´mero dado de veces: u >>> [0] * 4 [0, 0, 0, 0] >>> [1, 2, 3] * 3 [1, 2, 3, 1, 2, 3, 1, 2, 3]En el primer ejemplo la lista [0] contiene un solo elemento que es repetidocuatro veces. En el segundo ejemplo, la lista [1, 2, 3] se repite tres veces.
    • 90 Listas8.7. Porciones (slices)Las operaciones de porciones que vimos en la Secci´n 7.4 tambi´n funcionan en o esobre las listas: >>> lista = [’a’, ’b’, ’c’, ’d’, ’e’, ’f’] >>> lista[1:3] [’b’, ’c’] >>> lista[:4] [’a’, ’b’, ’c’, ’d’] >>> lista[3:] [’d’, ’e’, ’f’] >>> lista[:] [’a’, ’b’, ’c’, ’d’, ’e’, ’f’]8.8. Las listas son mutablesA diferencia de las cadenas, las listas son mutables, lo que significa que pode-mos cambiar sus elementos. Podemos modificar uno de sus elementos usando eloperador corchetes en el lado izquierdo de una asignaci´n: o>>> fruta = ["pl´tano", "manzana", "membrillo"] a>>> fruta[0] = "pera">>> fruta[-1] = "naranja">>> print fruta[’pera’, ’manzana’, ’naranja’]Con el operador de porci´n podemos reemplazar varios elementos a la vez: o >>> lista = [’a’, ’b’, ’c’, ’d’, ’e’, ’f’] >>> lista[1:3] = [’x’, ’y’] >>> print lista [’a’, ’x’, ’y’, ’d’, ’e’, ’f’]Adem´s, puede eliminar elementos de una lista asign´ndoles la lista vac´ a a ıa: >>> lista = [’a’, ’b’, ’c’, ’d’, ’e’, ’f’] >>> lista[1:3] = [] >>> lista [’a’, ’d’, ’e’, ’f’]Y puede a˜adir elementos a la lista embuti´ndolos en una porci´n vac´ en la n e o ıaposici´n deseada: o >>> lista = [’a’, ’d’, ’f’] >>> lista[1:1] = [’b’, ’c’] >>> print lista
    • 8.9 Borrado en una lista 91 [’a’, ’b’, ’c’, ’d’, ’f’] >>> lista[4:4] = [’e’] >>> print lista [’a’, ’b’, ’c’, ’d’, ’e’, ’f’]8.9. Borrado en una listaEl uso de porciones para borrar elementos de una lista puede ser extra˜o, y npor ello propicio a los errores. Python nos da una alternativa que resulta m´s alegible.del elimina un elemento de una lista:>>> a = [’uno’, ’dos’, ’tres’]>>> del a[1]>>> a[’uno’, ’tres’]Como podr´ esperar, del maneja ´ ıa ındices negativos y provoca un error en tiempode ejecuci´n sin el ´ o ındice est´ fuera de l´ a ımites.Puede usar una porci´n como ´ o ındice para del:>>> lista = [’a’, ’b’, ’c’, ’d’, ’e’, ’f’]>>> del lista[1:5]>>> print lista[’a’, ’f’]Como es habitual, las porciones seleccionan todos los elementos hasta, pero noinclu´ ıdo, el segundo ´ ındice.8.10. Objetos y valoresSi ejecutamos estas sentencias de asignaci´n: o a = "banana" b = "banana"Est´ claro que a y b apuntan ambos a cadenas con las letras "banana". Pero no apodemos saber si est´n apuntando a la misma cadena. aHay dos posibles estados:
    • 92 Listas a "banana" a "banana" b "banana" bEn un caso, a y b se refieren a dos cosas diferentes que tienen el mismo valor.En el segundo caso, se refieren a la misma cosa. Estas “cosas” tienen nombres;se les denomina objetos. Un objeto es una cosa a la que se puede referir unavariable.Cada objeto tiene un identificador unico, que podemos obtener por medio de ´la funci´n id. Imprimiendo los identificadores de a y b podemos saber si apuntan oal mismo objeto. >>> id(a) 135044008 >>> id(b) 135044008En este caso, las dos veces obtenemos el mismo identificador, lo que significaque Python s´lo cre´ una cadena y ambas variables, a y b, apuntan a ella. o oComo las cadenas de texto son inmutables, no hay diferencia pr´ctica entre los ados posibles estados. Para tipos mutables como las listas, s´ que importa. ıCuriosamente, las listas se comportan de otra manera. Cuando crea dos listas,obtiene dos objetos: >>> a = [1, 2, 3] >>> b = [1, 2, 3] >>> id(a) 135045528 >>> id(b) 135041704De manera que el diagrama de estado ser´ tal como ´ste: ıa e a [ 1, 2, 3 ] b [ 1, 2, 3 ]a y b tienen el mismo valor, pero no se refieren al mismo objeto.8.11. Alias (poner sobrenombres)Como las variables apuntan a objetos, si asigna una variable a otra, ambasvariables se refieren al mismo objeto:
    • 8.12 Clonar listas 93 >>> a = [1, 2, 3] >>> b = aEn este caso, el diagrama de estados ser´ como ´ste: ıa e a [ 1, 2, 3 ] bComo la misma lista tiene dos nombres diferentes, a y b, podemos decir que sele ha puesto un alias. Los cambios hechos a un alias afectan al otro: >>> b[0] = 5 >>> print a [5, 2, 3]Aunque este comportamiento puede ser util, a veces es inesperado o indeseable. ´En general, es m´s seguro evitar los alias cuando trabajemos con objetos muta- ables. Por supuesto, no hay problema con los objetos inmutables. Por ello Pythonse toma la libertad de poner alias a las cadenas cuando ve una oportunidad deeconomizar.8.12. Clonar listasSi queremos modificar una lista y mantener una copia del original, necesitaremosser capaces de hacer una copia de la lista en s´ no s´lo de su referencia. Este ı, oproceso a veces se denomina clonado, para evitar la ambig¨edad de la palabra u“copia”.La forma m´s f´cil de clonar una lista es por medio del operador de porci´n: a a o >>> a = [1, 2, 3] >>> b = [] >>> b[:] = a[:] >>> print b [1, 2, 3]La extracci´n de una porci´n de a crea una nueva lista. En este caso, la porci´n o o oconsta de la lista completa.Ahora tenemos libertad de hacer cambios en b sin preocuparnos de a: >>> b[0] = 5 >>> print a [1, 2, 3] Como ejercicio, dibuje un diagrama de estado de a y b antes y des- pues del cambio.
    • 94 Listas8.13. Listas como par´meteros aCuando se pasa una lista como argumento, en realidad se pasa una referenciaa ella, no una copia de la lista. Por ejemplo, la funci´n cabeza toma una lista ocomo par´metro y devuelve el primer elemento. a def cabeza(lista): return lista[0]As´ es como se usa. ı >>> numeros = [1,2,3] >>> cabeza(numeros) 1El par´metro lista y la variable numeros son alias de un mismo objeto. El adiagrama de estado es as´ ı: __main__ numbers [ 1, 2, 3 ] head listComo el objeto lista est´ compartido por dos marcos, lo dibujamos entre ambos. aSi la funci´n modifica una lista pasada como par´metro, el que hizo la llamada o aver´ el cambio. borra cabeza elimina el primer elemento de una lista. a def borra_cabeza(lista): del lista[0]Aqu´ vemos el uso de borra cabeza: ı >>> numeros = [1,2,3] >>> borra_cabeza(numeros) >>> print numeros [2, 3]Si una funci´n devuelve una lista, devuelve una referencia a la lista. Por ejemplo, ocola devuelve una lista que contiene todos los elementos de una lista dada,excepto el primero. def cola(lista): return lista[1:]Aqu´ vemos c´mo se usa cola: ı o >>> numeros = [1,2,3] >>> resto = cola(numeros) >>> print resto >>> [2, 3]
    • 8.14 Listas anidadas 95Como el valor de retorno se cre´ con una porci´n, es una lista. La creaci´n de o o orest, as´ como cualquier cambio posterior en rest, no afectar´ a numbers. ı a8.14. Listas anidadasUna lista anidada es una lista que aparece como elemento dentro de otra lista.En esta lista, el tri-´simo elemento es una lista anidada: e >>> lista = ["hola", 2.0, 5, [10, 20]]Si imprimimos lista[3], obtendremos [10, 20]. Para extraer los elementosde la lista anidada, podemos proceder en dos pasos: >>> elt = lista[3] >>> elt[0] 10O podemos combinarlos: >>> lista[3][1] 20Los operadores corchete se eval´an de izquierda a derecha, as´ que esta expresi´n u ı osaca el tri-´simo elemento de lista y luego extrae el un´simo elemento de ella. e e8.15. MatricesEs com´n usar listas anidadas para representar matrices. Por ejemplo, la matriz: u 1 2 3 4 5 6 7 8 9puede ser representada como: >>> matriz = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]matriz es una lista con tres elementos, siendo cada elemento una fila de lamatriz. Podemos elegir una fila entera de la matriz de la forma normal: >>> matriz[1] [4, 5, 6]O tomar s´lo un elemento de la matriz usando la forma de doble ´ o ındice: >>> matriz[1][1] 5
    • 96 ListasEl primer ´ ındice escoge la fila y el segundo la columna. Aunque esta manera derepresentar matrices es com´n, no es la unica posibilidad. Una peque˜a variaci´n u ´ n oconsiste en usar una lista de columnas en lugar de flas. M´s adelante veremos auna alternativa m´s radical usando un diccionario. a8.16. Cadenas y listasDos de las funciones m´s utiles del m´dulo string tienen que ver con listas a ´ ode cadenas. La funci´n split divide una cadena en una lista de palabras. Por odefecto, cualquier n´mero de caracteres de espacio en blanco se considera un ul´ ımite de palabra:>>> import string>>> cancion = "La lluvia en Sevilla...">>> string.split(cancion)[’La’, ’lluvia’, ’en’, ’Sevilla...’]Se puede usar un argumento opcional llamado delimitador para especificarqu´ caracteres se usar´n como l´ e a ımites de palabra. El siguiente ejemplo usa lacadena ll como delimitador:>>> string.split(cancion, ’ll’)[’La ’, ’uvia en Sevi’, ’a...’]Observe que el delimitador no aparece en la lista.La funci´n join es la inversa de split. Toma una lista de cadenas y concatena olos elementos con un espacio entre cada par:>>> lista = [’La’, ’lluvia’, ’en’, ’Sevilla...’]>>> string.join(lista)’La lluvia en Sevilla...’Como split, join acepta un delimitador opcional que se inserta entre los ele-mentos. El delimitador por defecto es el espacio.>>> string.join(lista, ’_’)’La_lluvia_en_Sevilla...’ A modo de ejercicio, describa la relaci´n que hay entre o string.join(string.split(cancion)) y cancion. ¿Es la misma para todas las cadenas? ¿Cu´ndo ser´ diferente? a ıa
    • 8.17 Glosario 978.17. Glosariolista: Una colecci´n de objetos con nombre, en la que cada objeto es identificado o por un ´ ındice.´ındice: Una variable o valor enteros que se usan para indicar un elemento de una lista.elemento: Uno de los valores de una lista (u otra secuencia). El operador cor- chete selecciona elementos de una lista.secuencia: Cualquier tipo de datos que consita en un conjunto ordenado de elementos, con cada elemento identificado por un ´ ındice.lista anidada: Una lista que es elemento de otra lista.recorrido de lista: Acceso secuencial a cada elemento de una lista.objeto: Una cosa a la que se puede referir una variable.alias: M´ltiples variables que contienen referencias al mismo objeto. uclonar: Crear un objeto nuevo que tiene el mismo valor que un objeto ya exis- tente. Copiar una referencia a un objeto crea un alias, pero no clona el objeto.delimitador: Un car´cter o cadena utilizado para indicar d´nde debe cortarse a o una cadena.
    • Cap´ ıtulo 9Tuplas9.1. Mutabilidad y tuplasHasta ahora, ha visto dos tipos compuestos: cadenas, que est´n hechas de ca- aracteres, y listas, que est´n hechas de elementos de cualquier tipo. Una de las adiferencias que se˜alamos es que los elementos de una lista se pueden modifi- ncar, pero los caracteres de una cadena no. En otras palabras, las cadenas soninmutables y las listas son mutables.En Python hay otro tipo llamado tupla que es similar a una lista salvo en quees inmutable. Sint´cticamente, una tupla es una lista de valores separados por acomas:>>> tupla = ’a’, ’b’, ’c’, ’d’, ’e’Aunque no es necesario, la convenci´n dice que hay que encerrar las tuplas entre opar´ntesis: e>>> tupla = (’a’, ’b’, ’c’, ’d’, ’e’)Para crear una tupla con un solo elemento, debemos incluir una coma final:>>> t1 = (’a’,)>>> type(t1)<type ’tuple’>Sin la coma, Python trata (´’) como una cadena entre par´ntesis: a e>>> t2 = (’a’)>>> type(t2)<type ’string’>
    • 100 TuplasDejando a un lado las cuestiones de sintaxis, las operaciones sobre las tuplasson las mismas que sobre las listas. El operador ´ ındice selecciona un elementode la tupla.>>> tupla = (’a’, ’b’, ’c’, ’d’, ’e’)>>> tupla[0]’a’Y el operador de porci´n selecciona un intervalo de elementos. o>>> tupla[1:3](’b’, ’c’)Pero si intentamos modificar uno de los elementos de la tupla provocaremos unerror:>>> tupla[0] = ’A’TypeError: object doesn’t support item assignmentPor supuesto, incluso aunque no podamos modificar los elementos de una tupla,podemos sustituir una tupla por otra diferente:>>> tupla = (’A’,) + tupla[1:]>>> tupla(’A’, ’b’, ’c’, ’d’, ’e’)9.2. Asignaci´n de tuplas oDe vez en cuando, es util intercambiar los valores de dos variables. Para ha- ´cerlo con sentencias de asignaci´n convencionales debemos usar una variable otemporal. Por ejemplo, para intercambiar a y b:>>> temp = a>>> a = b>>> b = tempSi tenemos que hacer esto a menudo, esta aproximaci´n resulta aparatosa. Pyt- ohon proporciona una forma de asignaci´n de tuplas que soluciona este pro- oblema elegantemente:>>> a, b = b, aEl lado izquierdo es una tupla de variables, el lado derecho es una tupla devalores. Cada valor se asigna a su respectiva variable. Todas las expresiones dellado derecho se eval´an antes de las asignaciones. Esta caracter´ u ıstica hace de laasignaci´n de tuplas algo muy vers´til. o a
    • 9.3 Tuplas como valor de retorno 101Naturalmente, el n´mero de variables a la izquierda y el n´mero de valores a la u uderecha deben ser iguales:>>> a, b, c, d = 1, 2, 3ValueError: unpack tuple of wrong size9.3. Tuplas como valor de retornoLas funciones pueden devolver tuplas como valor de retorno. Por ejemplo,podr´ ıamos escribir una funci´n que intercambie dos par´metros: o adef intercambio(x, y): return y, xLuego podemos asignar el valor de retorno a una tupla con dos variables:a, b = intercambio(a, b)En este caso, no hay ninguna ventaja en convertir intercambio en una funci´n.oDe hecho, existe un peligro al intentar encapsular intercambio, y es el tentadorerror que sigue:def intercambio(x, y): # versi´n incorrecta o x, y = y, xSi llamamos a esta funci´n as´ o ı:intercambio(a, b)a y x son alias del mismo valor. Cambiar x dentro de intercambio hace que xse refiera a un valor diferente, pero no tiene efecto alguno sobre a en main .De forma similar, cambiar y no tiene efecto sobre b.Esta funci´n se ejecuta sin generar un mensaje de error, pero no hace lo que ointentamos. Este es un ejemplo de error sem´ntico. a A modo de ejercicio, dibuje un diagrama de estados para esta funci´n o de manera que pueda ver por qu´ no trabaja como usted quiere. e9.4. N´ meros aleatorios uLa mayor parte de los programas hacen lo mismo cada vez que los ejecutamos,por lo que se dice que son deterministas. Normalmente el determinismo esuna cosa buena, ya que esperamos que un c´lculo nos d´ siempre el mismo a e
    • 102 Tuplasresultado. Para algunas aplicaciones, sin embargo, queremos que el computadorsea impredecible. El ejemplo obvio son los juegos, pero hay m´s. aHacer que un programa sea realmente no determinista resulta no ser tan sencillo,pero hay formas de que al menos parezca no determinista. Una de ellas es generarn´meros aleatorios y usarlos para determinar el resultado del programa. Python uproporciona una funci´n interna que genera n´meros pseudoaleatorios, que o uno son verdaderamente aleatorios en un sentido matem´tico, pero servir´n para a anuestros prop´sitos. oEl m´dulo random contiene una funci´n llamada random que devuelve un n´mero o o uen coma flotante entre 0,0 y 1,0. Cada vez que usted llama a random obtiene elsiguiente n´mero de una larga serie. Para ver un ejemplo, ejecute este bucle: uimport randomfor i in range(10): x = random.random() print xPara generar un n´mero aleatorio entre 0,0 y un l´ u ımite superior como maximo,multiplique x por maximo. Como ejercicio, genere un n´mero aleatorio entre minimo y maximo. u Como ejercicio adicional, genere un n´mero aleatorio entero entre u minimo y maximo, incluyendo ambos extremos.9.5. Lista de n´ meros aleatorios uEl primer paso es generar una lista de valores aleatorios. listaAleatoriosacepta un par´metro entero y devuelve una lista de n´meros aleatorios de la a ulongitud dada. Comienza con una lista de n ceros. Cada vez que ejecuta el bucle,sustituye uno de los elementos con un n´mero aleatorio. El valor de retorno es uuna referencia a la lista completa:def listaAleatorios(n): s = [0] * n for i in range(n): s[i] = random.random() return sVamos a probar esta funci´n con una lista de ocho elementos. A la hora de odepurar es una buena idea empezar con algo peque˜o. n
    • 9.6 Conteo 103>>> listaAleatorios(8)0.151566424890.4980485601090.8108948470680.3603711576820.2751191830770.3285787976310.7591998031010.800367163582Se supone que los n´meros generados por random est´n distribuidos uniforme- u amente, lo que significa que cada valor es igualmente probable.Si dividimos el intervalo de valores posibles en “baldes” de igual tama˜o y ncontamos el n´mero de veces que un valor cae en cada balde, deber´ u ıamos tenerm´s o menos el mismo n´mero en todos. a uPodemos contrastar esta teor´ escribiendo un programa que divida el intervalo ıaen baldes y contando el n´mero de valores en cada uno. u9.6. ConteoUn buen enfoque sobre problemas como ´ste es dividir el problema en subpro- eblemas que encajen en un esquema computacional que hayamos visto antes.En este caso, queremos recorrer una lista de n´meros y contar el n´mero de u uveces que un valor cae en un intervalo dado. Eso nos suena. En la Secci´n 7.8 oescribimos un programa que recorr´ una cadena de texto y contaba el n´mero ıa ude veces que aparec´ una letra determinada. ıaAs´ podemos hacerlo copiando el programa viejo y adapt´ndolo al problema ı, aactual. El programa original era:cuenta = 0for car in fruta: if car == ’a’: cuenta = cuenta + 1print cuentaEl primer paso es sustituir fruta con lista y car con num. Esto no cambia elprograma, s´lo lo hace m´s legible. o aEl segundo paso es cambiar la comprobaci´n. No estamos interesados en encon- otrar letras. Queremos ver si num est´ entre los valores de minimo y maximo. a
    • 104 Tuplascuenta = 0for num in lista if minimo < num < maximo: cuenta = cuenta + 1print cuentaEl ultimo paso es encapsular este c´digo en una funci´n llamada enElBalde. ´ o oLos par´metros son la lista y los valores minimo y maximo. adef enElBalde(lista, minimo, maximo): cuenta = 0 for num in lista: if minimo < num < maximo: cuenta = cuenta + 1 return cuentaCopiar y modificar un programa existente nos facilita escribir esta funci´n r´pi- o adamente y nos ahorra un mont´n de tiempo de depuraci´n. Este plan de desa- o orrollo se llama coincidencia de esquemas. Si se encuentra trabajando en unproblema que ya solucion´, reutilice la soluci´n. o o9.7. Muchos baldesTal como aumenta el n´mero de baldes, enElBalde se hace un tanto dif´ de u ıcilmanejar. Con dos baldes, no est´ mal: abajo = enElBalde(a, 0.0, 0.5)alto = enElBalde(a, 0.5, 1)Pero con cuatro baldes ya es aparatoso.balde1 = enElBalde(a, 0.0, 0.25)balde2 = enElBalde(a, 0.25, 0.5)balde3 = enElBalde(a, 0.5, 0.75)balde4 = enElBalde(a, 0.75, 1.0)Hay dos problemas. Uno es que tenemos que inventar nuevos nombres de va-riables para cada resultado. El otro es que tenemos que calcular el intervalo decada balde.Empezaremos por solucionar el segundo problema. Si el n´mero de baldes es unumBaldes, la anchura de cada balde es 1.0 / numBaldes.Usaremos un bucle para calcular el intervalo de cada balde. La variable delbucle, i, cuenta de 1 a numBaldes-1:
    • 9.7 Muchos baldes 105anchuraBalde = 1.0 / numBaldesfor i in range(numBaldes): minimo = i * anchuraBalde maximo = minimo + anchuraBalde print minimo, "hasta", maximoPara calcular el l´ ımite inferior de cada balde, multiplicamos la variable de buclepor la anchura de balde. El l´ ımite superior est´ a tan s´lo una anchuraBalde. a oCon numBaldes = 8, la salida es:0.0 hasta 0.1250.125 hasta 0.250.25 hasta 0.3750.375 hasta 0.50.5 hasta 0.6250.625 hasta 0.750.75 hasta 0.8750.875 hasta 1.0Puede confirmar que todos los bucles tienen la misma anchura, que no se solapany que cubren todo el intervalo entre 0,0 y 1,0.Volvamos ahora al primer problema. Necesitamos un modo de almacenar ochoenteros, usando la variable de bucle para se˜alarlos uno por uno. En estos mo- nmentos deber´ usted estar pensando “¡Lista!”. ıaDebemos crear la lista de baldes fuera del bucle, porque s´lo queremos hacer- olo una vez. Dentro del bucle, podemos llamar repetidamente a enElBalde yactualizar el i-´simo elemento de la lista: enumBaldes = 8baldes = [0] * numBaldesanchuraBalde = 1.0 / numBaldesfor i in range(numBaldes): minimo = i * anchuraBalde maximo = minimo + anchuraBalde baldes[i] = enElBalde(lista, minimo, maximo)print baldesCon una lista de 1000 valores, este c´digo genera esta lista de baldes: o[138, 124, 128, 118, 130, 117, 114, 131]Estos n´meros son razonablemente pr´ximos a 125, que es lo que esper´bamos u o aPor lo menos, est´n lo bastante cerca como para que podamos pensar que el agenerador de n´meros aleatorios funciona. u
    • 106 Tuplas Como ejercicio, compruebe esta funci´n con listas m´s largas, y vea o a si el n´mero de valores en cada balde tiende a equilibrarse. u9.8. Una soluci´n en una sola pasada oAunque este programa funciona, no es tan eficiente como podr´ ser. Cada vez ıaque llama a enElBalde recorre la lista entera. Con el aumento del n´mero de ubaldes, llega a ser un mont´n de recorridos. oSer´ mejor hacer una sola pasada por la lista y calcular para cada valor el ´ ıa ındicedel balde en el que cae. Luego podemos incrementar el contador apropiado.En la secci´n anterior tomamos un ´ o ındice, i, y lo multiplicamos por laanchuraBalde para hallar el l´ımite inferior de un balde dado. Ahora quere-mos tomar un valor del intervalo 0,0 a 1,0 y hallar el ´ ındice del balde en el quecae.Como el problema es el inverso del anterior, podemos suponer que deber´ ıamosdividir por anchuraBalde en lugar de multiplicar. La suposici´n es correcta. oComo anchuraBalde = 1.0 / numBaldes, dividir por anchuraBalde es lo mis-mo que multiplicar por numBaldes. Si multiplicamos un n´mero del intervalo uque va de 0,0 a 1,0 por numBaldes, obtenemos un n´mero del intervalo entre u0,0 y numBaldes. Si redondeamos ese n´mero al entero inferior obtendremos uexactamente lo que estamos buscando, un ´ ındice de balde:numBaldes = 8baldes = [0] * numBaldesfor i in lista: indice = int(i * numBaldes) baldes[indice] = baldes[indice] + 1Usamos la funci´n int para convertir un n´mero en coma flotante en un entero. o u¿Es posible que este c´lculo genere un ´ a ındice que est´ fuera del intervalo (tanto enegativo como mayor que len(baldes)-1)?Una lista como baldes que contiene conteos del n´mero de valores en cada uintervalo se llama histograma. Como ejercicio, escriba una funci´n llamada histograma que tome o como par´metros una lista y un n´mero de baldes y devuelva un a u histograma con el n´mero dado de baldes. u
    • 9.9 Glosario 1079.9. Glosariotipo inmutable: Un tipo en el cual los elementos no se puede modificar. Las asignaciones de elementos o porciones de tipos inmutables provocan un error.tipo mutable: Un tipo de datos en el cual los elementos pueden ser modifi- cados. Todos los tipos mutables son compuestos. Las listas y diccionarios son tipos de datos mutables, las cadenas y las tuplas no.tupla: Un tipo de secuencia que es similar a una lista excepto en que es in- mutable. Las tuplas se pueden usar donde quiera que se necesite un tipo inmutable, como puede ser la clave de un diccionario.asignaci´n de tuplas: Una asignaci´n de todos los elementos de una tupla o o usando una unica sentencia de asignaci´n. La asignaci´n de tuplas sucede ´ o o m´s bien en paralelo que secuencialmente, haci´ndola util para intercam- a e ´ biar valores.determinista: Un programa que hace lo mismo todas las veces que se ejecuta.pseudoaleatorio: Una secuencia de n´meros que parece ser aleatoria pero que u en realidad es el resultado de un c´lculo determinista. ahistograma: Una lista de enteros en la que cada elemento cuenta el n´mero u de veces que ocurre algo.coincidencia de esquemas: Un plan de desarrollo de programas que implica la identificaci´n de un esquema computacional conocido y el copiado de o la soluci´n para un problema similar. o
    • Cap´ ıtulo 10DiccionariosLos tipos compuestos que ha visto hasta ahora (cadenas, listas y tuplas) usan en-teros como ´ındices. Si intenta usar cualquier otro tipo como ´ ındice provocar´ un aerror.Los diccionarios son similares a otros tipos compuestos excepto en que puedenusar como ´ındice cualquier tipo inmutable. A modo de ejemplo, crearemos undiccionario que traduzca palabras inglesas al espa˜ol. En este diccionario, los n´ındices son strings (cadenas).Una forma de crear un diccionario es empezar con el diccionario vac´ y a˜adir ıo nelementos. El diccionario vac´ se expresa como {}: ıo>>> ing_a_esp = {}>>> ing_a_esp[’one’] = ’uno’>>> ing_a_esp[’two’] = ’dos’La primera asignaci´n crea un diccionario llamado ing a esp; las otras asig- onaciones a˜aden nuevos elementos al diccionario. Podemos presentar el valor nactual del diccionario del modo habitual:>>> print ing_a_esp{’one’: ’uno’, ’two’: ’dos’}Los elementos de un diccionario aparecen en una lista separada por comas.Cada entrada contiene un ´ ındice y un valor separado por dos puntos (:). En undiccionario, los ´ ındices se llaman claves, por eso los elementos se llaman paresclave-valor.Otra forma de crear un diccionario es dando una lista de pares clave-valor conla misma sintaxis que la salida del ejemplo anterior:
    • 110 Diccionarios>>> ing_a_esp = {’one’: ’uno’, ’two’: ’dos’, ’three’: ’tres’}Si volvemos a imprimir el valor de ing a esp, nos llevamos una sorpresa:>>> print ing_a_esp{’one’: ’uno’, ’three’: ’tres’, ’two’: ’dos’}¡Los pares clave-valor no est´n en orden! Afortunadamente, no necesitamos preo- acuparnos por el orden, ya que los elementos de un diccionario nunca se indexancon ´ ındices enteros. En lugar de eso, usamos las claves para buscar los valorescorrespondientes:>>> print ing_a_esp[’two’]’dos’La clave ’two’ nos da el valor ’dos’ aunque aparezca en el tercer par clave-valor.10.1. Operaciones sobre diccionariosLa sentencia del elimina un par clave-valor de un diccionario. Por ejemplo, eldiccionario siguiente contiene los nombres de varias frutas y el n´mero de esas ufrutas en el almac´n: e>>> inventario = {’manzanas’: 430, ’bananas’: 312,... ’naranjas’: 525, ’peras’: 217}>>> print inventario{’naranjas’: 525, ’manzanas’: 430, ’peras’: 217, ’bananas’: 312}Si alguien compra todas las peras, podemos eliminar la entrada del diccionario:>>> del inventario[’peras’]>>> print inventario{’naranjas’: 525, ’manzanas’: 430, ’bananas’: 312}O si esperamos recibir m´s peras pronto, podemos simplemente cambiar el in- aventario asociado con las peras:>>> inventario[’peras’] = 0>>> print inventario{’naranajas’: 525, ’manzanas’: 430, ’peras’: 0, ’bananas’: 312}La funci´n len tambi´n funciona con diccionarios; devuelve el n´mero de pares o e uclave-valor:>>> len(inventario)4
    • 10.2 M´todos del diccionario e 11110.2. M´todos del diccionario eUn m´todo es similar a una funci´n, acepta par´metros y devuelve un valor, e o apero la sintaxis es diferente. Por ejemplo, el m´todo keys acepta un diccionario ey devuelve una lista con las claves que aparecen, pero en lugar de la sintaxis dela funci´n keys(ing a esp), usamos la sintaxis del m´todo ing a esp.keys(). o e>>> ing_a_esp.keys()[’one’, ’three’, ’two’]Esta forma de notaci´n de punto especifica el nombre de la funci´n, keys, y el o onombre del objeto al que se va a aplicar la funci´n, ing a esp. Los par´ntesis o eindican que este m´todo no admite par´metros. e aLa llamda a un m´todo se denomina invocaci´n; en este caso, dir´ e o ıamos queestamos invocando keys sobre el objeto ing a esp.El m´todo values es similar; devuelve una lista de los valores del diccionario: e>>> ing_a_esp.values()[’uno’, ’tres’, ’dos’]El m´todo items devuelve ambos, una lista de tuplas con los pares clave-valor edel diccionario:>>> ing_a_esp.items()[(’one’,’uno’), (’three’, ’tres’), (’two’, ’dos’)]La sintaxis nos proporciona informaci´n muy util acerca del tipo de datos. Los o ´corchetes indican que es una lista. Los par´ntesis indican que los elementos de ela lista son tuplas.Si un m´todo acepta un argumento, usa la misma sintaxis que una llamada ea una funci´n. Por ejemplo, el m´todo has key acepta una clave y devuelve o everdadero (1) si la clave aparece en el diccionario:>>> ing_a_esp.has_key(’one’)1>>> ing_a_esp.has_key(’deux’)0Si usted invoca un m´todo sin especificar un objeto, provoca un error. En este ecaso, el mensaje de error no es de mucha ayuda:>>> has_key(’one’)NameError: has_key
    • 112 Diccionarios10.3. Asignaci´n de alias y copiado oDebe usted estar atento a los alias a causa de la mutabilidad de los diccionarios.Si dos variables se refieren al mismo objeto los cambios en una afectan a la otra.Si quiere modificar un diccionario y mantener una copia del original, use elm´todo copy. Por ejemplo, opuestos es un diccionario que contiene pares de eopuestos:>>> opuestos = {’arriba’: ’abajo’, ’derecho’: ’torcido’,... ’verdadero’: ’falso’}>>> alias = opuestos>>> copia = opuestos.copy()alias y opuestos se refieren al mismo objeto; copia hace referencia a una copianueva del mismo diccionario. Si modificamos alias, opuestos tambi´n resulta ecambiado:>>> alias[’derecho’] = ’sentado’>>> opuestos[’derecho’]’sentado’Si modificamos copia, opuestos no var´ ıa:>>> copia[’derecho’] = ’privilegio’>>> opuestos[’derecho’]’sentado’10.4. Matrices dispersasEn la Secci´n 8.14 usamos una lista de listas para representar una matriz. Es ouna buena opci´n para una matriz en la que la mayor´ de los valores es diferente o ıade cero, pero piense en una matriz como ´sta: e 0 0 0 1 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 3 0La representaci´n de la lista contiene un mont´n de ceros: o omatriz = [ [0,0,0,1,0], [0,0,0,0,0],
    • 10.5 Pistas 113 [0,2,0,0,0], [0,0,0,0,0], [0,0,0,3,0] ]Una posible alternativa es usar un diccionario. Como claves, podemos usar tuplas u ´que contengan los n´meros de fila y columna. Esta es la representaci´n de la omisma matriz por medio de un diccionario:matriz = {(0,3): 1, (2, 1): 2, (4, 3): 3}S´lo hay tres pares clave-valor, una para cada elemento de la matriz diferente ode cero. Cada clave es una tupla, y cada valor es un entero.Para acceder a un elemento de la matriz, podemos usar el operador []:matriz[0,3]1F´ıjese en que la sintaxis para la representaci´n por medio del diccionario no es ola misma de la representaci´n por medio de la lista anidada. En lugar de dos o´ındices enteros, usamos un ´ ındice que es una tupla de enteros.Hay un porblema. Si apuntamos a un elemento que es cero, se produce un errorporque en el diccionario no hay una entrada con esa clave:>>> matriz[1,3]KeyError: (1, 3)El m´todo get soluciona este problema: e>>> matriz.get((0,3), 0)1El primer argumento es la clave; el segundo argumento es el valor que debedevolver get en caso de que la clave no est´ en el diccionario: e>>> matriz.get((1,3), 0)0get mejora sensiblemente la sem´ntica del acceso a una matriz dispersa. L´stima a ade sintaxis.10.5. PistasSi estuvo jugando con la funci´n fibonacci de la Secci´n 5.7, es posible que o ohaya notado que cuanto m´s grande es el argumento que le da, m´s tiempo le a a
    • 114 Diccionarioscuesta ejecutarse. M´s a´n, el tiempo de ejecuci´n aumenta muy r´pidamente. a u o aEn nuestra m´quina, fibonacci(20) acaba instant´neamente, fibonacci(30) a atarda m´s o menos un segundo, y fibonacci(40) tarda una eternidad. aPara entender por qu´, observe este gr´fico de llamadas de fibonacci con e an=4: fibonacci n 4 fibonacci fibonacci n 3 n 2 fibonacci fibonacci fibonacci fibonacci n 2 n 1 n 1 n 0 fibonacci fibonacci n 1 n 0Un gr´fico de llamadas muestra un conjunto de cajas de funci´n con l´ a o ıneas queconectan cada caja con las cajas de las funciones a las que llama. En lo altodel gr´fico, fibonacci con n=4 llama a fibonacci con n=3 y n=2. A su vez, afibonacci con n=3 llama a fibonacci con n=2 y n=1. Y as´ sucesivamente. ıCuente cu´ntas veces se llama a fibonacci(0) y fibonacci(1). Es una soluci´n a oineficaz al problema, y empeora mucho tal como crece el argumento.Una buena soluci´n es llevar un registro de los valores que ya se han calculado oalmacen´ndolos en un diccionario. A un valor que ya ha sido calculado y alma- acenado para un uso posterior se le llama pista. Aqu´ hay una implementaci´n ı ode fibonacci con pistas:anteriores = {0:1, 1:1}def fibonacci(n): if anteriores.has_key(n): return anteriores[n] else: nuevoValor = fibonacci(n-1) + fibonacci(n-2) anteriores[n] = nuevoValor return nuevoValorEl diccionario llamado anteriores mantiene un registro de los valores de Fibo-nacci que ya conocemos. El programa comienza con s´lo dos pares: 0 corresponde oa 1 y 1 corresponde a 1.
    • 10.6 Enteros largos 115Siempre que se llama a fibonacci comprueba si el diccionario contiene el resul-tado ya calculado. Si est´ ah´ la funci´n puede devolver el valor inmediatamente a ı, osin hacer m´s llamadas recursivas. Si no, tiene que calcular el nuevo valor. El anuevo valor se a˜ade al diccionario antes de que la funci´n vuelva. n oCon esta versi´n de fibonacci, nuestra m´quina puede calcular fibonacci(40) o aen un abrir y cerrar de ojos. Pero cuando intentamos calcular fibonacci(50),nos encontramos con otro problema:>>> fibonacci(50)OverflowError: integer additionLa respuesta, como ver´ en un momento, es 20.365.011.074. El problema es aque este n´mero es demasiado grande para caber en un entero de Python. Se udesborda. Afortunadamente, hay una soluci´n f´cil para este problema. o a10.6. Enteros largosPython proporciona un tipo llamado long int que puede manejar enteros decualquier tama˜o. Hay dos formas de crear un valor long int. Una es escribir nun entero con una L may´scula al final: u>>> type(1L)<type ’long int’>La otra es usar la funci´n long para convertir un valor en long int. long acepta ocualquier tipo num´rico e incluso cadenas de d´ e ıgitos:>>> long(1)1L>>> long(3.9)3L>>> long(’57’)57LTodas las operaciones matem´ticas funcionan sobre los long ints, as´ que no a ıtenemos que hacer mucho para adaptar fibonacci:>>> previous = {0:1L, 1:1L}>>> fibonacci(50)20365011074LSimplemente cambiando el contenido inicial de anteriores cambiamos el com-portamiento de fibonacci. Los primeros dos n´meros de la secuencia son long uints, as´ que todos los n´meros subsiguientes lo ser´n tambi´n. ı u a e
    • 116 Diccionarios Como ejercicio, modifique factorial de forma que produzca un long int como resultado.10.7. Contar letrasEn el cap´ıtulo 7 escribimos una funci´n que contaba el n´mero de apariciones o ude una letra en una cadena. Una versi´n m´s gen´rica de este problema es crear o a eun histograma de las letras de la cadena, o sea, cu´ntas veces aparece cada letra. aEse histograma podr´ ser util para comprimir un archivo de texto. Como las ıa ´diferentes letras aparecen con frecuencias distintas, podemos comprimir un ar-chivo usando c´digos cortos para las letras m´s habituales y c´digos m´s largos o a o apara las que aparecen con menor frecuencia.Los diccionarios facilitan una forma elegante de generar un histograma:>>> cuentaLetras = {}>>> for letra in "Mississippi":... cuentaLetras[letra] = cuentaLetras.get (letra, 0) + 1...>>> cuentaLetras{’M’: 1, ’s’: 4, ’p’: 2, ’i’: 4}>>>Inicialmente, tenemos un diccionario vac´ Para cada letra de la cadena, bus- ıo.camos el recuento actual (posiblemente cero) y lo incrementamos. Al final, eldiccionario contiene pares de letras y sus frecuencias.Puede ser m´s atractivo mostrar el histograma en orden alfab´tico. Podemos a ehacerlo con los m´todos items y sort: e>>> itemsLetras = cuentaLetras.items()>>> itemsLetras.sort()>>> print itemsLetras[(’M’, 1), (’i’, 4), (’p’, 2), (’s’, 4)]Ya hab´ visto usted el m´todo items, pero sort es el primer m´todo aplicable ıa e ea listas que hemos visto. Hay varios m´s, como append, extend, y reverse. aConsulte la documentaci´n de Python para ver los detalles. o10.8. Glosariodiccionario: Una colecci´n de pares clave-valor que establece una correspon- o dencia entre claves y valores. Las claves pueden ser de cualquier tipo in- mutable, los valores pueden ser de cualquier tipo.
    • 10.8 Glosario 117clave: Un valor que se usa para buscar una entrada en un diccionario.par clave-valor: Uno de los elementos de un diccionario, tambi´n llamado e “asociaci´n”. om´todo: Un tipo de funci´n al que se llama con una sintaxis diferente y al que e o se invoca “sobre” un objeto.invocar: Llamar a un m´todo. epista: Almacenamiento temporal de un valor precalculado para evitar c´lculos a redundantes.desbordamiento: Un resultado num´rico que es demasiado grande para re- e presentarse en formato num´rico. e
    • Cap´ ıtulo 11Archivos y excepcionesCuando un programa se est´ ejecutando, sus datos est´n en la memoria. Cuando a aun programa termina, o se apaga el computador, los datos de la memoria desa-parecen. Para almacenar los datos de forma permanente debe usted ponerlos enun archivo. Normalmente los archivos se guardan en un disco duro, disquete oCD-ROM.Cuando hay un gran n´mero de archivos, suelen estar organizados en directo- urios (tambi´n llamados “carpetas”). Cada archivo se identifica con un nombre eunico, o una combinaci´n de nombre de archivo y nombre de directorio.´ oLeyendo y escribiendo archivos, los programas pueden intercambiar informaci´n oentre ellos y generar formatos imprimibles como PDF.Trabajar con archivos se parece mucho a trabajar con libros. Para usar unlibro, tiene que abrirlo. Cuando ha terminado, tiene que cerrarlo. Mientras ellibro est´ abierto, puede escribir en ´l o leer de ´l. En cualquier caso, sabe en a e equ´ lugar del libro se encuentra. Casi siempre lee el libro seg´n su orden natural, e upero tambi´n puede ir saltando de una p´gina a otra. e aTodo esto sirve tambi´n para los archivos. Para abrir un archivo, especifique su enombre e indique si quiere leer o escribir.La apertura de un archivo crea un objeto archivo. En este ejemplo, la variablef apunta al nuevo objeto archivo.>>> f = open("test.dat","w")>>> print f<open file ’test.dat’, mode ’w’ at fe820>
    • 120 Archivos y excepcionesLa funci´n open toma dos argumentos. El primero es el nombre del archivo y oel segundo es el modo. El modo ’w’ (write) significa que lo estamos abriendopara escribir.Si no hay un archivo llamado test.dat se crear´. Si ya hay uno, el archivo que aestamos escribiendo lo reemplazar´. aAl imprimir el objeto archivo, vemos el nombre del archivo, el modo y la loca-lizaci´n del objeto. oPara meter datos en el archivo invocamos al m´todo write sobre el objeto earchivo:>>> f.write("Ya es hora")>>> f.write("de cerrar el archivo")El cierre del archivo le dice al sistema que hemos terminado de escribir y dejael archivo listo para leer:>>> f.close()Ya podemos abrir el archivo de nuevo, esta vez para lectura, y poner su contenidoen una cadena. Esta vez el argumento de modo es ’r’ (read) para lectura:>>> f = open("test.dat","r")Si intentamos abrir un archivo que no existe, recibimos un mensaje de error:>>> f = open("test.cat","r")IOError: [Errno 2] No such file or directory: ’test.cat’Como era de esperar, el m´todo read lee datos del archivo. Sin argumentos, lee eel archivo completo:>>> text = f.read()>>> print textYa es horade cerrar el archivoNo hay un espacio entre “hora” y “de” porque no escribimos un espacio entrelas cadenas.read tambi´n puede aceptar un argumento que le indica cu´ntos caracteres leer: e a>>> f = open("test.dat","r")>>> print f.read(7)Ya es hSi no quedan suficientes caracteres en el archivo, read devuelve los que haya.Cuando llegamos al final del archivo, read devuelve una cadena vac´ ıa:
    • 11.1 Archivos de texto 121>>> print f.read(1000006)orade cerrar el archivo>>> print f.read()>>>La siguiente funci´n copia un archivo, leyendo y escribiendo los caracteres de ocincuenta en cincuenta. El primer argumento es el nombre del archivo original;el segundo es el nombre del archivo nuevo:def copiaArchivo(archViejo, archNuevo): f1 = open(archViejo, "r") f2 = open(archNuevo, "w") while 1: texto = f1.read(50) if texto == "": break f2.write(texto) f1.close() f2.close() returnLa sentencia break es nueva. Su ejecuci´n interrumpe el bucle; el flujo de la oejecuci´n pasa a la primera sentencia tras el bucle. oEn este ejmplo, el bucle while es infinito porque el valor 1 siempre es verdadero.La unica forma de salir del bucle es ejecutar break, lo que sucede cuando texto ´es una cadena vac´ lo que sucede cuando llegamos al final del archivo. ıa,11.1. Archivos de textoUn archivo de texto es un archivo que contiene caracteres imprimibles y es-pacios organizados en l´ ıneas separadas por caracteres de salto de l´ ınea. ComoPython est´ dise˜ado espec´ a n ıficamente para procesar archivos de texto, propor-ciona m´todos que facilitan la tarea. ePara hacer una demostraci´n, crearemos un archivo de texto con tres l´ o ıneas detexto separadas por saltos de l´ ınea:>>> f = open("test.dat","w")>>> f.write("l´nea unonl´nea dosnl´nea tresn") ı ı ı>>> f.close()El m´todo readline lee todos los caracteres hasta e inclusive el siguiente salto ede l´ ınea:
    • 122 Archivos y excepciones>>> f = open("test.dat","r")>>> print f.readline()l´nea uno ı>>>readlines devuelve todas las l´ ıneas que queden como una lista de cadenas:>>> print f.readlines()[’l´nea dos012’, ’l´nea tres012’] ı ıEn este caso, la salida est´ en forma de lista, lo que significa que las cadenas aaparecen con comillas y el car´cter de salto de l´ a ınea aparece como la secuenciade escape012.Al final del archivo, readline devuelve una cadena vac´ y readlines devuelve ıauna lista vac´ ıa:>>> print f.readline()>>> print f.readlines()[]Lo que sigue es un ejemplo de un programa de proceso de l´ ıneas. filtraArchivohace una copia de archViejo, omitiendo las l´ıneas que comienzan por #:def filtraArchivo(archViejo, archNuevo): f1 = open(archViejo, "r") f2 = open(archNuevo, "w") while 1: texto = f1.readline() if texto == "": break if texto[0] == ’#’: continue f2.write(texto) f1.close() f2.close() returnLa sentencia continue termina la iteraci´n actual del bucle, pero sigue haciendo obucles. El flujo de ejecuci´n pasa al principio del bucle, comprueba la condici´n o oy contin´a en consecuencia. u
    • 11.2 Escribir variables 123As´ si texto es una cadena vac´ el bucle termina. Si el primer car´cter de ı, ıa, atexto es una almohadilla, el flujo de ejecuci´n va al principio del bucle. S´lo si o oambas condiciones fallan copiamos texto en el archivo nuevo.11.2. Escribir variablesEl argumento de write debe ser una cadena, as´ que si queremos poner otros ıvalores en un archivo, tenemos que convertirlos antes en cadenas. La forma m´s af´cil de hacerlo es con la funci´n str: a o>>> x = 52>>> f.write (str(x))Una alternativa es usar el operador de formato %. Cuando aplica a enteros, %es el operador de m´dulo. Pero cuando el primer operando es una cadena, % es oel operador de formato.El primer operando es la cadena de formato, y el segundo operando es unatupla de expresiones. El resultado es una cadena que contiene los valores de lasexpresiones, formateados de acuerdo a la cadena de formato.A modo de ejemplo simple, la secuencia de formato ’%d’ significa que laprimera expresi´n de la tupla deber´ formatearse como un entero. Aqu´ la letra o ıa ıd quiere decir “decimal”:>>> motos = 52>>> "%d" % motos’52’El resultado es la cadena ’52’, que no debe confundirse con el valor entero 52.Una secuencia de formato puede aparecer en cualquier lugar de la cadena deformato, de modo que podemos incrustar un valor en una frase:>>> motos = 52>>> "En julio vendimos %d motos." % motos’En julio vendimos 52 motos.’La secuencia de formato ’%f’ formatea el siguiente elemento de la tupla comoun n´mero en coma flotante, y ’%s’ formatea el siguiente elemento como una ucadena:>>> "En %d d´as ingresamos %f millones de %s." ı % (34,6.1,’d´lares’) o’En 34 d´as ingresamose 6.100000 miliones de d´lares.’ ı o
    • 124 Archivos y excepcionesPor defecto, el formato de coma flotante imprime seis decimales.El n´mero de expresiones en la tupla tiene que coincidir con el n´mero de u usecuencias de formato de la cadena. Igualmente, los tipos de las expresionesdeben coincidir con las secuencias de formato:>>> "%d %d %d" % (1,2)TypeError: not enough arguments for format string>>> "%d" % ’d´lares’ oTypeError: illegal argument type for built-in operationEn el primer ejemplo, no hay suficientes expresiones; en el segundo, la expresi´n oes de un tipo incorrecto.Para tener m´s control sobre el formato de los n´meros, podemos detallar el a un´mero de d´ u ıgitos como parte de la secuencia de formato:>>> "%6d" % 62’ 62’>>> "%12f" % 6.1’ 6.100000’El n´mero tras el signo de porcentaje es el n´mero m´ u u ınimo de espacios queocupar´ el n´mero. Si el valor necesita menos d´ a u ıgitos, se a˜aden espacios en nblanco delante del n´mero. Si el n´mero de espacios es negativo, se a˜aden los u u nespacios tras el n´mero: u>>> "%-6d" % 62’62 ’Tambi´n podemos especificar el n´mero de decimales para los n´meros en coma e u uflotante:>>> "%12.2f" % 6.1’ 6.10’En este ejemplo, el resultado ocupa doce espacios e incluye dos d´ ıgitos tras lacoma. Este formato es util para imprimir cantidades de dinero con las comas ´alineadas.Imagine, por ejemplo, un diccionario que contiene los nombres de los estudiantescomo clave y las tarifas horarias como valores. He aqu´ una funci´n que imprime ı oel contenido del diccionario como un informe formateado:def informe (tarifas) : estudiantes = tarifas.keys() estudiantes.sort()
    • 11.3 Directorios 125 for estudiante in estudiantes : print "%-20s %12.02f" % (estudiante, tarifas[estudiante])Para probar la funci´n, crearemos un peque˜o diccionario e imprimiremos el o ncontenido:>>> tarifas = {’mar´a’: 6.23, ’jos´’: 5.45, ’jes´s’: 4.25} ı e u>>> informe (tarifas)jos´ e 5.45jes´s u 4.25mar´a ı 6.23Controlando la anchura de cada valor nos aseguramos de que las columnas van aquedar alineadas, siempre que los nombres tengan menos de veinti´n caracteres uy las tarifas sean menos de mil millones la hora.11.3. DirectoriosCuando usted crea un archivo nuevo abri´ndolo y escribiendo, el nuevo archi- evo va al directorio en uso (aqu´l en el que etuviese al ejecutar el programa). eDel mismo modo, cuando abre un archivo para leerlo, Python lo busca en eldirectorio en uso.Si quiere abrir un archivo de cualquier otro sitio, tiene que especificar la rutadel archivo, que es el nombre del directorio (o carpeta) donde se encuentra ´ste: e>>> f = open("/usr/share/dict/words","r")>>> print f.readline()AarhusEste ejemplo abre un archivo llamado words que est´ en un directorio llamado adict, que est´ en share, que est´ en usr, que est´ en el directorio de nivel a a asuperior del sistema, llamado /.No puede usar / como parte del nombre de un archivo; est´ reservado como adelimitador entre nombres de archivo y directorios.El archivo /usr/share/dict/words contiene una lista de palabras en ordenalfab´tico, la primera de las cuales es el nombre de una universidad danesa. e11.4. EncurtidoPara poner valores en un archivo, debe convertirlos en cadenas. Ya ha vistoc´mo hacerlo con str: o
    • 126 Archivos y excepciones>>> f.write (str(12.3))>>> f.write (str([1,2,3]))El problema es que cuando vuelve usted a leer el valor, obtiene una cadena. Haperdido la informaci´n del tipo de dato original. En realidad, no puede distinguir od´nde termina un valor y comienza el siguiente: o>>> f.readline()’12.3[1, 2, 3]’La soluci´n es el encurtido, llamado as´ porque “conserva” estructuras de datos. o ıEl m´dulo pickle contiene las ´rdenes necesarias. Para usarlo, importe pickle o oy luego abra el archivo de la forma habitual:>>> import pickle>>> f = open("test.pck","w")Para almacenar una estructura de datos, use el m´todo dump y luego cierre el earchivo de la forma habitual:>>> pickle.dump(12.3, f)>>> pickle.dump([1,2,3], f)>>> f.close()Ahora podemos abrir el archivo para leer y cargar las estructuras de datos quevolcamos ah´ ı:>>> f = open("test.pck","r")>>> x = pickle.load(f)>>> x12.3>>> type(x)<type ’float’>>>> y = pickle.load(f)>>> y[1, 2, 3]>>> type(y)<type ’list’>Cada vez que invocamos load obtenemos un valor del archivo, completo con sutipo original.11.5. ExcepcionesSiempre que ocurre un error en tiempo de ejecuci´n, se crea una excepci´n. o oNormalmente el programa se para y Pythton presenta un mensaje de error.
    • 11.5 Excepciones 127Por ejemplo, la divisi´n por cero crea una excepci´n: o o>>> print 55/0ZeroDivisionError: integer division or moduloUn elemento no existente en una lista hace lo mismo:>>> a = []>>> print a[5]IndexError: list index out of rangeO el acceso a una clave que no est´ en el diccionario: a>>> b = {}>>> print b[’qu´’] eKeyError: qu´ eEn cada caso, el mensaje de error tiene dos partes: el tipo de error antes delos dos puntos y detalles sobre el error depu´s de los dos puntos. Normalmente ePython tambi´n imprime una traza de d´nde se encontraba el programa, pero e ola hemos omitido en los ejemplos.A veces queremos realizar una operaci´n que podr´ provocar una excepci´n, o ıa opero no queremos que se pare el programa. Podemos manejar la excepci´n ousando las sentencias try y except.Por ejemplo, podemos preguntar al usuario por el nombre de un archivo y luegointentar abrirlo. Si el archivo no existe, no queremos que el programa se pare;queremos manejar la excepci´n. onombreArch = raw_input(’Introduce un nombre de archivo: ’)try: f = open (nombreArch, "r")except: print ’No hay ning´n archivo que se llame’, nombreArch uLa sentencia try ejecuta las sentencias del primer bloque. Si no se produce nin-guna excepci´n, pasa por alto la sentencia except. Si ocurre cualquier excepci´n, o oejecuta las sentencias de la rama except y despu´s contin´a. e uPodemos encapsular esta capacidad en una funci´n: existe acepta un nombre ode archivo y devuelve verdadero si el archivo existe y falso si no:def existe(nombreArch): try: f = open(nombreArch) f.close()
    • 128 Archivos y excepciones return 1 except: return 0Puede usar m´ltiples bloques except para manejar diferentes tipos de excep- uciones. El Manual de Referencia de Python contiene los detalles.Si su programa detecta una condici´n de error, puede hacer que lance (raise en oingl´s) una excepci´n. Aqu´ tiene usted un ejemplo que acepta una entrada del e o ıusuario y comprueba si es 17. Suponiendo que 17 no es una entrada v´lida por acualquier raz´n, lanzamos una excepci´n. o odef tomaNumero () : # Recuerde, los acentos est´n a x = input (’Elige un n´mero: ’) u # prohibidos en los nombres if x == 17 : # de funciones y variables! raise ’ErrorN´meroMalo’, ’17 es u un mal n´mero’ u return xLa sentencia raise acepta dos argumentos: el tipo de excepci´n e informaci´n o oespec´ ıfica acerca del error. ErrorN´meroMalo es un nuevo tipo de excepci´n que u ohemos inventado para esta aplicaci´n. oSi la funci´n llamada tomaNumero maneja el error, el programa puede continuar; oen caso contrario, Python imprime el mensaje de error y sale:>>> tomaNumero ()Elige un n´mero: 17 uErrorN´meroMalo: 17 es un mal n´mero u uEl mensaje de error incluye el tipo de excepci´n y la informaci´n adicional que o ousted proporcion´. o Como ejercicio, escriba una funci´n que use tomaNumero para leer o un n´mero del teclado y que maneje la excepci´n ErrorN´meroMalo. u o u11.6. Glosarioarchivo: Una entidad con nombre, normalmente almacenada en un disco duro, disquete o CD-ROM, que contiene una secuencia de caracteres.directorio: Una colecci´n, con nombre, de archivos, tambi´n llamado carpeta. o eruta: Una secuencia de nombres de directorio que especifica la localizaci´n o exacta de un archivo.
    • 11.6 Glosario 129archivo de texto: Un archivo que contiene caracteres imprimibles organizados en l´ ıneas separadas por caracteres de salto de l´ ınea.sentencia break: Una sentencia que provoca que el flujo de ejecuci´n salga de o un bucle.sentencia continue: Una sentencia que provoca que termine la iteraci´n ac-o tual de un bucle. El flujo de la ejecuci´n va al principio del bucle, eval´a o u la condici´n, y procede en consecuencia. ooperador de formato: El operador % toma una cadena de formato y una tupla de expresiones y entrega una cadena que incluye las expresiones, forma- teadas de acuerdo con la cadena de formato.cadena de formato: Una cadena que contiene caracteres imprimibles y se- cuencias de formato que indican c´mo formatear valores. osecuencia de formato: Una secuencia de caracteres que comienza con % e in- dica c´mo formatear un valor. oencurtir: Escribir el valor de un dato en un archivo junto con la informaci´n o sobre su tipo de forma que pueda ser reconstituido m´s tarde. aexcepci´n: Un error que ocurre en tiempo de ejecuci´n. o omanejar: Impedir que una excepci´n detenga un programa utilizando las sen- o tencias try y except.lanzar: Se˜alar una excepci´n usando la sentencia raise. n o
    • Cap´ ıtulo 12Clases y objetos12.1. Tipos compuestos definidos por el usuarioUna vez utilizados algunos de los tipos internos de Python, estamos listos paracrear un tipo definido por el usuario: el Punto.Piense en el concepto de un punto matem´tico. En dos dimensiones, un punto es ados n´meros (coordenadas) que se tratan colectivamente como un solo objeto. uEn notaci´n matem´tica, los puntos suelen escribirse entre par´ntesis con una o a ecoma separando 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 elorigen.Una forma natural de representar un punto en Python es con dos valores encoma flotante. La cuesti´n es, entonces, c´mo agrupar esos dos valores en un o oobjeto compuesto. La soluci´n r´pida y burda es utilizar una lista o tupla, y o apara algunas aplicaciones esa podr´ ser la mejor opci´n. ıa oUna alternativa es que el usuario defina un nuevo tipo compuesto, tambi´n ellamado una clase. Esta aproximaci´n exige un poco m´s de esfuerzo, pero o atiene sus ventajas que pronto se har´n evidentes. aUna definici´n de clase se parece a esto: oclass Punto: passLas definiciones de clase pueden aparecer en cualquier lugar de un programa,pero normalmente est´n al principio (tras las sentencias import). Las reglas a
    • 132 Clases y objetossint´cticas de la definici´n de clases son las mismas que para cualesquiera otras a osentencias compuestas. (ver la Secci´n 4.4). oEsta definici´n crea una nueva clase llamada Punto. La sentencia pass no tiene oefectos; s´lo es necesaria porque una sentencia compuesta debe tener algo en su ocuerpo.Al crear la clase Punto hemos creado un nuevo tipo, que tambi´n se llama Punto. eLos miembros de este tipo se llaman instancias del tipo u objetos. La creaci´n ode una nueva instancia se llama instanciaci´n. Para instanciar un objeto Punto oejecutamos una funci´n que se llama (lo ha adivinado) Punto: oblanco = Punto()A la variable blanco se le asigna una referencia a un nuevo objeto Punto. Auna funci´n como Punto que crea un objeto nuevo se le llama constructor. o12.2. AtributosPodemos a˜adir nuevos datos a una instancia utilizando la notaci´n de punto: n o>>> blanco.x = 3.0>>> blanco.y = 4.0Esta sintaxis es similar a la sintaxis para seleccionar una variable de un m´du- olo, como math.pi o string.uppercase. En este caso, sin embargo, estamosseleccionando un dato de una instancia. Estos ´ ıtemes con nombre se llamanatributos.El diagrama de estados que sigue muestra el resultado de esas asignaciones: blanco x 3.0 y 4.0La variable blanco apunta a un objeto Punto, que contiene dos atributos. Cadaatributo apunta a un n´mero en coma flotante. uPodemos leer el valor de un atributo utilizando la misma sintaxis:>>> print blanco.y4.0>>> x = blanco.x>>> print x3.0
    • 12.3 Instancias como par´metro a 133La expresi´n blanco.x significa, “ve al objeto al que apunta blanco y toma el ovalor de x”. En este caso, asignamos ese valor a una variable llamada x. No hayconflicto entre la variable x y el atributo x. El prop´sito de la notaci´n de punto o oes identificar de forma inequ´ ıvoca a qu´ variable se refiere. ePuede usted usar la notaci´n de punto como parte de cualquier expresi´n. As´ o o ı,las sentencias que siguen son correctas:print ’(’ + str(blanco.x) + ’, ’ + str(blanco.y) + ’)’distanciaAlCuadrado = blanco.x * blanco.x + blanco.y * blanco.yLa primera l´ ınea presenta (3.0, 4.0); la segunda l´ ınea calcula el valor 25.0.Puede tentarle imprimir el propio valor de blanco:>>> print blanco<__main__.Punto instance at 80f8e70>El resultado indica que blanco es una instancia de la clase Punto que se de-fini´ en main . 80f8e70 es el identificador unico de este objeto, escrito en o ´hexadecimal. Probablemente no es esta la manera m´s clara de mostrar un aobjeto Punto. En breve ver´ c´mo cambiarlo. a o Como ejercicio, cree e imprima un objeto Punto y luego use id pa- ra imprimir el identificador unico del objeto. Traduzca el n´mero ´ u hexadecimal a decimal y aseg´rese de que coinciden. u12.3. Instancias como par´metro aPuede usted pasar una instancia como par´metro de la forma habitual. Por aejemplo:def imprimePunto(p): print ’(’ + str(p.x) + ’, ’ + str(p.y) + ’)’imprimePunto acepta un punto como argumento y lo muestra en formatoest´ndar. Si llama a imprimePunto(blanco), el resultado es (3.0, 4.0). a Como ejercicio, reescriba la funci´n distancia de la Secci´n 5.2 de o o forma que acepte dos Puntos como par´metros en lugar de cuatro a n´meros. u
    • 134 Clases y objetos12.4. MismidadEl significado de la palabra “mismo” parece totalmente claro hasta que uno separa un poco a pensarlo, y entonces se da cuenta de que hay algo m´s de lo que asupon´ıa.Por ejemplo, si dice “Pepe y yo tenemos la misma moto”, lo que quiere decir esque su moto y la de usted son de la misma marca y modelo, pero que son dosmotos distintas. Si dice “Pepe y yo tenemos la misma madre”, quiere decir quesu madre y la de usted son la misma persona1 . As´ que la idea de “identidad” ıes diferente seg´n el contexto. uCuando habla de objetos, hay una ambig¨edad parecida. Por ejemplo, si dos uPuntos son el mismo, ¿significa que contienen los mismos datos (coordenadas)o que son de verdad el mismo objeto?Para averiguar si dos referencias se refieren al mismo objeto, utilice el operador==. Por ejemplo:>>> p1 = Punto()>>> p1.x = 3>>> p1.y = 4>>> p2 = Punto()>>> p2.x = 3>>> p2.y = 4>>> p1 == p20Aunque p1 y p2 contienen las mismas coordenadas, no son el mismo objeto. Siasignamos p1 a p2, las dos variables son alias del mismo objeto:>>> p2 = p1>>> p1 == p21Este tipo de igualdad se llama igualdad superficial porque s´lo compara las oreferencias, pero no el contenido de los objetos.Para comparar los contenidos de los objetos (igualdad profunda) podemosescribir una funci´n llamada mismoPunto: odef mismoPunto(p1, p2) : return (p1.x == p2.x) and (p1.y == p2.y) 1 No todas las lenguas tienen el mismo problema. Por ejemplo, el alem´n tiene palabras adiferentes para los diferentes tipos de identidad. “Misma moto” en este contexto ser´ “gleiche ıaMotorrad” y “misma madre” ser´ “selbe Mutter”. ıa
    • 12.5 Rect´ngulos a 135Si ahora creamos dos objetos diferentes que contienen los mismos datos podre-mos usar mismoPunto para averiguar si representan el mismo punto:>>> p1 = Punto()>>> p1.x = 3>>> p1.y = 4>>> p2 = Punto()>>> p2.x = 3>>> p2.y = 4>>> mismoPunto(p1, p2)1Por supuesto, si las dos variables apuntan al mismo objeto mismoPunto devuelveverdadero.12.5. Rect´ngulos aDigamos que queremos una clase que represente un rect´ngulo. La pregunta es, a¿qu´ informaci´n tenemos que proporcionar para definir un rect´ngulo? Para e o asimplificar las cosas, supongamos que el rect´ngulo est´ orientado vertical u a ahorizontalmente, nunca en diagonal.Tenemos varias posibilidades: podemos se˜alar el centro del rect´ngulo (dos n acoordenadas) y su tama˜o (anchura y altura); o podemos se˜alar una de las n nesquinas y el tama˜o; o podemos se˜alar dos esquinas opuestas. Un modo con- n nvencional es se˜alar la esquina superior izquierda del rect´ngulo y el tama˜o. n a nDe nuevo, definiremos una nueva clase:class Rectangulo: # Prohibidos los acentos fuera de las cadenas! passY la instanciaremos:caja = Rectangulo()caja.anchura = 100.0caja.altura = 200.0Este c´digo crea un nuevo objeto Rectangulo con dos atributos en coma flo- otante. ¡Para se˜alar la esquina superior izquierda podemos incrustar un objeto ndentro de otro!caja.esquina = Punto()caja.esquina.x = 0.0;caja.esquina.y = 0.0;
    • 136 Clases y objetosEl operador punto compone. La expresi´n caja.esquina.x significa “ve al ob- ojeto al que se refiere caja y selecciona el atributo llamado esquina; entonces vea ese objeto y selecciona el atributo llamado x”.La figura muestra el estado de este objeto: caja anchura 100.0 altura 200.0 x 0.0 esquina y 0.012.6. Instancias como valores de retornoLas funciones pueden devolver instancias. Por ejemplo, encuentraCentro acep-ta un Rectangulo como argumento y devuelve un Punto que contiene las coor-denadas del centro del Rectangulo:def encuentraCentro(caja): p = Punto() p.x = caja.esquina.x + caja.anchura/2.0 p.y = caja.esquina.y + caja.altura/2.0 return pPara llamar a esta funci´n, pase caja como argumento y asigne el resultado a ouna variable:>>> centro = encuentraCentro(caja)>>> imprimePunto(centro)(50.0, 100.0)12.7. Los objetos son mudablesPodemos cambiar el estado de un objeto efectuando una asignaci´n sobre uno ode sus atributos. Por ejemplo, para cambiar el tama˜o de un rect´ngulo sin n acambiar su posici´n, podemos cambiar los valores de anchura y altura: ocaja.anchura = caja.anchura + 50caja.altura = caja.altura + 100Podemos encapsular este c´digo en un m´todo y generalizarlo para agrandar el o erect´ngulo en cualquier cantidad: a
    • 12.8 Copiado 137def agrandaRect(caja, danchura, daltura) : caja.anchura = caja.anchura + danchura caja.altura = caja.altura + dalturaLas variables danchura y daltura indican cu´nto debe agrandarse el rect´ngulo a aen cada direcci´n. Invocar este m´todo tiene el efecto de modificar el Rectangulo o eque se pasa como argumento.Por ejemplo, podemos crear un nuevo Rectangulo llamado bob y pas´rselo a aagrandaRect:>>> bob = Rectangulo()>>> bob.anchura = 100.0>>> bob.altura = 200.0>>> bob.esquina = Punto()>>> bob.esquina.x = 0.0;>>> bob.esquina.y = 0.0;>>> agrandaRect(bob, 50, 100)Mientras agrandaRect se est´ ejecutando, el par´metro caja es un alias de bob. a aCualquier cambio que haga a caja afectar´ tambi´n a bob. a e A modo de ejercicio, escriba una funci´n llamada mueveRect que o tome un Rectangulo y dos par´metros llamados dx y dy. Tiene que a cambiar la posici´n del rect´ngulo a˜adiendo dx a la coordenada x o a n de esquina y a˜adiendo dy a la coordenada y de esquina. n12.8. CopiadoEl uso de alias puede hacer que un programa sea dif´ de leer, porque los ıcilcambios hechos en un lugar pueden tener efectos inesperados en otro lugar. Esdif´ estar al tanto de todas las variables a las que puede apuntar un objeto ıcildado.Copiar un objeto es, muchas veces, una alternativa a la creaci´n de un alias. El om´dulo copy contiene una funci´n llamada copy que puede duplicar cualquier o oobjeto:>>> import copy>>> p1 = Punto()>>> p1.x = 3>>> p1.y = 4>>> p2 = copy.copy(p1)>>> p1 == p2
    • 138 Clases y objetos 0 >>> mismoPunto(p1, p2) 1 Una vez que hemos importado el m´dulo copy, podemos usar el m´todo copy o e para hacer un nuevo Punto. p1 y p2 no son el mismo punto, pero contienen los mismos datos. Para copiar un objeto simple como un Punto, que no contiene objetos incrusta- dos, copy es suficiente. Esto se llama copiado superficial. Para algo como un Rectangulo, que contiene una referencia a un Punto, copy no lo hace del todo bien. Copia la referencia al objeto Punto, de modo que tanto el Rectangulo viejo como el nuevo apuntan a un unico Punto. ´ Si creamos una caja, b1, de la forma habitual y entonces hacemos una copia, b2, usando copy, el diagrama de estados resultante se ve as´ ı:b1 anchura 100.0 100.0 anchura b2 altura 200.0 x 0.0 200.0 altura esquina y 0.0 esquina Es casi seguro que esto no es lo que queremos. En este caso, la invocaci´n de o agrandaRect sobre uno de los Rectangulos no afectar´ al otro, ¡pero la invo- ıa caci´n de mueveRect sobre cualquiera afectaria a ambos! Este comportamiento o es confuso y propicia los errores. Afortunadamente, el m´dulo copy contiene un m´todo llamado deepcopy que o e copia no s´lo el objeto sino tambi´n cualesquiera objetos incrustados. No le o e sorprender´ saber que esta operaci´n se llama copia profunda (deep copy). a o >>> b2 = copy.deepcopy(b1) Ahora b1 y b2 son objetos totalmente independientes. Podemos usar deepcopy para reescribir agrandaRect de modo que en lugar de modificar un Rectangulo existente, cree un nuevo Rectangulo que tiene la misma localizaci´n que el viejo pero nuevas dimensiones: o def agrandaRect(caja, danchura, daltura) : import copy nuevaCaja = copy.deepcopy(caja) nuevaCaja.anchura = nuevaCaja.anchura + danchura nuevaCaja.altura = nuevaCaja.altura + daltura return nuevaCaja
    • 12.9 Glosario 139 Como ejercicio, resscriba mueveRect de modo que cree y devuelva un nuevo Rectangulo en lugar de modificar el viejo.12.9. Glosarioclase: Un tipo compuesto definido por el usuario. Tambi´n se puede pensar e en una clase como una plantilla para los objetos que son instancias de la misma.instanciar: Crear una instancia de una clase.instancia: Un objeto que pertenece a una clase.objeto: Un tipo de dato compuesto que suele usarse para representar una cosa o concepto del mundo real.constructor: Un m´todo usado para crear nuevos objetos. eatributo: Uno de los elementos de datos con nombre que constituyen una ins- tancia.igualdad superficial: Igualdad de referencias, o dos referencias que apuntan al mismo objeto.igualdad profunda: Igualdad de valores, o dos referencias que apuntan a ob- jetos que tienen el mismo valor.copia superficial: Copiar el contenido de un objeto, incluyendo cualquier refe- rencia a objetos incrustados; implementada por la funci´n copy del m´dulo o o copy.copia profunda: Copiar el contenido de un objeto as´ como cualesquiera ob- ı jetos incrustados, y los incrustados en estos, y as´ sucesivamente; imple- ı mentada por la funci´n deepcopy del m´dulo copy. o o
    • Cap´ ıtulo 13Clases y funciones13.1. HoraComo otro ejemplo de un tipo definido por el usuario, definiremos una clasellamada Hora que registra la hora del d´ La definici´n de la clase es como ıa. osigue:class Hora: passPodemos crear un nuevo objeto Hora y asignar atributos para contener las horas,minutos y segundos:hora = Hora()hora.horas = 11hora.minutos = 59hora.segundos = 30El diagrama de estado del objeto Hora es as´ ı: time hour 11 minute 59 second 30 A modo de ejercicio, escriba una funci´n imprimeHora que acep- o te un objeto Hora como argumento y lo imprima en el formato horas:minutos:segundos.
    • 142 Clases y funciones Como un segundo ejercicio, escriba una funci´n booleana despues o que tome dos objetos Hora, t1 y t2, como argumentos y devuelva verdadero (1) si t1 sigue cronol´gicamente a t2 y falso (0) en caso o contrario.13.2. Funciones purasEn las pr´ximas secciones, escribiremos dos versiones de una funci´n llamada o osumaHora que calcule la suma de dos Horas. Mostrar´n dos tipos de funciones: afunciones puras y modificadores.´Este es un esbozo de sumaHora:def sumaHora(t1, t2): suma = Hora() suma.horas = t1.horas + t2.horas suma.minutos = t1.minutos + t2.minutos suma.segundos = t1.segundos + t2.segundos return sumaLa funci´n crea un nuevo objeto Hora, inicializa sus atributos y devuelve una oreferencia al nuevo objeto. A esto se le llama funci´n pura porque no modifica oninguno de los objetos que se le pasan y no tiene efectos laterales, como mostrarun valor o tomar una entrada del usuario.Aqu´ tiene un ejemplo de c´mo usar esta funci´n. Crearemos dos objetos Hora: ı o ohoraActual, que contiene la hora actual, y horaPan, que contiene la cantidadde tiempo que necesita un panadero para hacer pan. Luego usaremos sumaHorapara averiguar cu´ndo estar´ hecho el pan. Si a´n no ha terminado de escribir a a uimprimeHora, eche un vistazo a la Secci´n 14.2 antes de probar esto: o>>> horaActual = Hora()>>> horaActual.horas = 9>>> horaActual.minutos = 14>>> horaActual.segundos = 30>>> horaPan = Hora()>>> horaPan.horas = 3>>> horaPan.minutos = 35>>> horaPan.segundos = 0>>> horaHecho = sumaHora(horaActual, horaPan)>>> imprimeHora(horaHecho)
    • 13.3 Modificadores 143La salida de este programa es 12:49:30, lo que es correcto. Por otra parte, haycasos en los que el resultado no es correcto. ¿Puede imaginar uno?El problema es que esta funci´n no trata los casos en los que el n´mero de o usegundos o minutos suma m´s que sesenta. Cuando ocurre eso, debemos “llevar” alos segundos sobrantes a la columna de los minutos o los minutos extras a lacolumna de las horas.He aqu´ una versi´n corregida de la funci´n: ı o odef sumaHora(t1, t2): suma = Hora() suma.horas = t1.horas + t2.horas suma.minutos = t1.minutos + t2.minutos suma.segundos = t1.segundos + t2.segundos if suma.segundos >= 60: suma.segundos = suma.segundos - 60 suma.minutos = suma.minutos + 1 if suma.minutos >= 60: suma.minutos = suma.minutos - 60 suma.horas = suma.horas + 1 return sumaAunque esta funci´n es correcta, empieza a ser grande. M´s adelante sugeriremos o auna aproximaci´n alternativa que nos dar´ un c´digo m´s corto. o a o a13.3. ModificadoresHay veces en las que es util que una funci´n modifique uno o m´s de los objetos ´ o aque recibe como par´metros. Normalmente, el llamante conserva una referencia aa los objetos que pasa, as´ que cualquier cambio que la funci´n haga ser´ visible ı o apara el llamante. Las funciones que trabajan as´ se llaman modificadores. ıincremento, que a˜ade un n´mero dado de segundos a un objeto Hora, se n uescribir´ de forma natural como un modificador. Un esbozo r´pido de la funci´n ıa a opodr´ ser ´ste: ıa e
    • 144 Clases y funcionesdef incremento(hora, segundos): hora.segundos = hora.segundos + segundos if hora.segundos >= 60: hora.segundos = hora.segundos - 60 hora.minutos = hora.minutos + 1 if hora.minutos >= 60: hora.minutos = hora.minutos - 60 hora.horas = hroa.horas + 1La primera l´ınea realiza la operaci´n b´sica, las restantes tratan con los casos o aespeciales que vimos antes.¿Es correcta esta funci´n? ¿Qu´ ocurre si el par´metro segundos es mucho o e amayor que sesenta? En tal caso, no es suficiente con acarrear una vez; debemosseguir haci´ndolo hasta que segundos sea menor que sesenta. Una soluci´n es e osustituir las sentencias if por sentencias while:def incremento(hora, segundos): hora.segundos = hora.segundos + segundos while hora.segundos >= 60: hora.segundos = hora.segundos - 60 hora.minutos = hora.minutos + 1 while hora.minutos >= 60: hora.minutos = hora.minutos - 60 hora.horas = hroa.horas + 1Ahora esta funci´n es correcta, pero no es la soluci´n m´s eficiente. o o a Como ejercicio, reescriba esta funci´n de modo que no contenga tan- o tos bucles. Como un segundo ejercicio, reescriba incremento como una funci´n o pura, y escriba una funci´n que llame a ambas versiones. o13.4. ¿Qu´ es mejor? eTodo lo que se pueda hacer con modificadores puede hacerse tambi´n con fun- eciones puras. En realidad, algunos lenguajes de programaci´n s´lo permiten o ofunciones puras. Hay ciertas evidencias de que los programas que usan funcio-nes puras son m´s r´pidos de desarrollar y menos propensos a los errores que a a
    • 13.5 Desarrollo de prototipos frente a planificaci´n o 145los programas que usan modificadores. Sin embargo, a veces los modificadoresson utiles, y en algunos casos los programas funcionales son menos eficientes. ´En general, recomendamos que escriba funciones puras siempre que sea razona-ble hacerlo as´ y recurra a los modificadores s´lo si hay una ventaja convincente. ı oEste enfoque podr´ llamarse estilo funcional de programaci´n. ıa o13.5. Desarrollo de prototipos frente a planifi- caci´n oEn este cap´ ıtulo mostramos una aproximaci´n al desarrollo de programas a la oque llamamos desarrollo de prototipos. En cada caso, escribimos un esbozobasto (o prototipo) que realizaba el c´lculo b´sico y luego lo probamos sobre a aunos cuantos casos, corrigiendo los fallos tal como los encontr´bamos. aAunque este enfoque puede ser efecitvo, puede conducirnos a c´digo que es oinnecesariamente complicado, ya que trata con muchos casos especiales, y pocofiable, porque es dif´ saber si encontr´ todos los errores. ıcil oUna alternativa es el desarrollo planificado, en el que una comprensi´n del oproblema en profundidad puede hacer la programaci´n mucho m´s f´cil. En este o a acaso, el enfoque es que un objeto Hora es en realidad ¡un n´mero de tres d´ u ıgitosen base 60! El componente segundo es la “columna de unidades”, el componenteminuto es la “columna de las sesententas” y el componente hora es la “columnade las tresmilseiscentenas”.Cuando escribimos sumaHora e incremento, en realidad est´bamos haciendo auna suma en base 60, que es por lo que deb´ ıamos acarrear de una columna a lasiguiente.Esta observaci´n sugiere otro enfoque para el problema. Podemos convertir un oobjeto Hora en un simple n´mero y sacar provecho del hecho de que la m´quina u asabe la aritm´tica necesaria. La siguiente funci´n convierte un objeto Hora en e oun entero:def convierteASegundos(t): minutos = t.horas * 60 + t.minutos segundos = minutos * 60 + t.segundos return segundosAhora, s´lo necesitamos una forma de convertir un entero en un objeto Hora: odef haceHora(segundos): hora = Hora() hora.horas = segundos/3600
    • 146 Clases y funciones segundos = segundos - hora.horas * 3600 hora.minutos = segundos/60 segundos = segundos - hora.minutos * 60 hora.segundos = segundos return horaPuede que tenga usted que pensar un poco para convencerse de que esta t´cni- eca para convertir de una base a otra es correcta. Suponiendo que est´ usted aconvencido, puede usar estas funciones para reescribir sumaHora:def sumaHora(t1, t2): segundos = convierteASegundos(t1) + convierteASegundos(t2) return haceHora(segundos)Esta versi´n es mucho m´s corta que la original, y es mucho m´s f´cil de de- o a a amostrar que es correcta (suponiendo, como es habitual, que las funciones a lasque llama son correctas). Como ejercicio, reescriba incremento de la misma forma.13.6. Generalizaci´n oDe alg´n modo, convertir de base 60 a base 10 y de vuelta es m´s dif´ que u a ıcilsimplemente manejarse con las horas. La conversi´n de base es m´s abstracta; o anuestra intuici´n para tratar con las horas es mejor. oPero si tenemos la comprensi´n para tratar las horas como n´meros en base 60, y o uhacer la inversi´n de escribir las funciones de conversi´n (convierteASegundos o oy haceHora), obtenemos un programa que es m´s corto, m´s f´cil de leer y a a adepurar y m´s fiable. aTambi´n es m´s f´cil a˜adir funcionalidades m´s tarde. Por ejemplo, imagine e a a n arestar dos Horas para hallar el intervalo entre ellas. La aproximaci´n ingenua oser´ implementar la resta con acarreo. Con el uso de las funciones de conversi´n ıa oser´ m´s f´cil y con mayor probabilidad, correcto. a a aIr´nicamente, a veces hacer un poblema m´s complejo (o m´s general) lo hace o a am´s f´cil (porque hay menos casos especiales y menos oportunidades de error). a a13.7. AlgoritmosCuando escribe una soluci´n general para una clase de problemas, en contraste ocon una soluci´n espec´ o ıfica a un problema concreto, ha escrito un algoritmo.
    • 13.8 Glosario 147Mencionamos esta palabra antes pero no la definimos con precisi´n. No es f´cil o ade definir, as´ que probaremos un par de enfoques. ıPrimero, piense en algo que no es un algoritmo. Cuando usted aprendi´ a mul- otiplicar n´meros de una cifra, probablemente memoriz´ la tabla de multiplicar. u oEn efecto, memoriz´ 100 soluciones espec´ o ıficas. Ese tipo de conocimiento no esalgor´ıtmico.Pero si usted era “harag´n” probablemente hizo trampa aprendiendo algunos atrucos. Por ejemplo, para encontrar el producto de n por 9, puede escribir n −1 como el primer d´ ıgito y 10 − n como el segundo d´ ıgito. Este truco es unasoluci´n general para multiplicar cualquier n´mero de una cifra por 9. ¡Eso es o uun algoritmo!De forma similar, las t´cnicas que aprendi´ para la suma y la resta con aca- e orreo y la divisi´n larga son todas algoritmos. Una de las caracter´ o ısticas de losalgoritmos es que no requieren inteligencia para llevarse a cabo. Son procesosmec´nicos en los que cada paso sigue al anterior de acuerdo a un conjunto simple ade reglas.En nuestra opini´n, es un poco vergonzoso que los humanos pasen tanto tiempo oen la escuela aprendiendo a ejecutar algoritmos que, de forma bastante similar,no exigen inteligencia.Por otra parte, el proceso de dise˜ar algoritmos es interesante, un desaf´ inte- n ıolectual y una parte primordial de lo que llamamos programar.Algunas de las cosas que la gente hace naturalmente, sin dificultad ni pensa-miento consciente, son las m´s dif´ a ıciles de expresar algor´ ıtmicamente. Entenderel lenguaje natural es un buen ejemplo. Todos lo hacemos, pero hasta el momen-to nadie ha sido capaz de explicar c´mo lo hacemos, al menos no en la forma ode un algoritmo.13.8. Glosariofunci´n pura: Una funci´n que no modifica ninguno de los objetos que recibe o o como par´metros. La mayor´ de las funciones puras son rentables. a ıamodificador: Una funci´n que modifica uno o m´s de los objetos que recibe o a como par´metros. La mayor´ de los modificadores no entregan resultado. a ıaestilo funcional de programaci´n: Un estilo de programaci´n en el que la o o mayor´ de las funciones son puras. ıadesarrollo de prototipos: Una forma de desarrollar programas empezando con un prototipo y prob´ndolo y mejor´ndolo gradualmente. a a
    • 148 Clases y funcionesdesarrollo planificado: Una forma de desarrollar programas que implica una comprensi´n de alto nivel del problema y m´s planificaci´n que desarrollo o a o incremental o desarrollo de prototipos.algoritmo: Un conjunto de instrucciones para solucionar una clase de proble- mas por medio de un proceso mec´nico sin intervenci´n de inteligencia. a o
    • Cap´ ıtulo 14Clases y m´todos e14.1. Caracter´ ısticas de la orientaci´n a objetos oPython es un lenguaje de programaci´n orientado a objetos, lo que signi- ofica que porporciona caracter´ ısticas que apoyan la programaci´n orientada oa objetos.No es f´cil definir la programaci´n orientada a objetos, pero ya hemos visto a oalgunas de sus caracter´ ısticas: Los programas se hacen a base de definiciones de objetos y definiciones de funciones, y la mayor parte de la computaci´n se expresa en t´rminos de o e operaciones sobre objetos. Cada definici´n de un objeto se corresponde con un objeto o concepto del o mundo real, y las funciones que operan en ese objeto se corresponden con las formas en que interact´an los objetos del mundo real. uPor ejemplo, la clase Hora definida en el Cap´ ıtulo 13 se corresponde con la formaen la que la gente registra la hora del d´ y las funciones que definimos se corres- ıa,ponden con el tipo de cosas que la gente hace con las horas. De forma similar,las clases Punto y Rectangulo se corresponden con los conceptos matem´ticos ade un punto y un rect´ngulo. aHasta ahora, no nos hemos aprovechado de las caracter´ ısticas que Python nosofrece para dar soporte a la programaci´n orientada a objetos. Hablando estric- otamente, estas caracter´ısticas no son necesarias. En su mayor´ proporcionan ıa,una sintaxis alternativa para cosas que ya hemos hecho, pero en muchos casos,
    • 150 Clases y m´todos ela alternativa es m´s concisa y expresa con m´s precisi´n a la estructura del a a oprograma.Por ejemplo, en el programa Hora no hay una conexi´n obvia entre la definici´n o ode la clase y las definiciones de las funciones que siguen. Observando bien, sehace patente que todas esas funciones toman al menos un objeto Hora comopar´metro. aEsta observaci´n es la que motiva los m´todos. Ya hemos visto varios m´todos, o e ecomo keys y values, que se invocan sobre diccionarios. Cada m´todo est´ aso- e aciado con una clase y est´ pensado para invocarse sobre instancias de esa clase. aLos m´todos son como las funciones, con dos diferencias: e Los m´todos se definen dentro de una definici´n de clase para explicitar e o la relaci´n entre la clase y el m´todo. o e La sintaxis para invocar un m´todo es diferente de la de una llamada a e una funci´n. oEn las pr´ximas secciones tomaremos las funciones de los cap´ o ıtulos anteriores ylas transformaremos en m´todos. Esta transformaci´n es puramente mec´nica; e o apuede hacerla simplemente siguiendo una secuencia de pasos. Si se acostumbraa convertir de una forma a la otra ser´ capaz de elegir la mejor forma de hacer alo que quiere.14.2. imprimeHoraEn el Cap´ ıtulo 13, definimos una clase llamada Hora y escribimos una fuci´n ollamada imprimeHora, que deber´ ser parecida a esto: ıaclass Hora: passdef imprimeHora(hora): print str(hora.horas) + ":" + str(hora.minutos) + ":" + str(hora.segundos)Para llamar a esta funci´n, pas´bamos un objeto Hora como par´metro: o a a>>> horaActual = Hora()>>> horaActual.horas = 9>>> horaActual.minutos = 14>>> horaActual.segundos = 30>>> impriemHora(horaActual)
    • 14.3 Otro ejemplo 151Para convertir imprimeHora en un m´todo, todo lo que necesitamos hacer es emover la definici´n de la funci´n al interior de la definici´n de la clase. F´ o o o ıjeseen c´mo cambia el sangrado. oclass Hora: def imprimeHora(hora): print str(hora.horas) + ":" + str(hora.minutos) + ":" + str(hora.segundos)Ahora podemos invocar imprimeHora usando la notaci´n de punto. o>>> horaActual.imprimeHora()Como es habitual, el objeto sobre el que se invoca el m´todo aparece delante edel punto y el nombre del m´todo aparece tras el punto. eEl objeto sobre el que se invoca el m´todo se asigna al primer par´metro, as´ que e a ıen este caso horaActual se asigna al par´metro hora. aPor convenio, el primer par´metro de un m´todo se llama self. La raz´n de a e oesto es un tanto rebuscada, pero se basa en una met´fora util. a ´La sintaxis para la llamada a una funci´n, imprimeHora(horaActual), sugiere oque la funci´n es el agente activo. Dice algo como “¡Oye imprimeHora! Aqu´ hay o ıun objeto para que lo imprimas”.En programaci´n orientada a objetos, los objetos son los agentes activos. Una in- ovocaci´n como horaActual.imprimeHora() dice “¡Oye horaActual! ¡Impr´ o ıme-te!”Este cambio de perspectiva puede ser m´s elegante, pero no es obvio que sea autil. En los ejemplos que hemos visto hasta ahora, puede no serlo. Pero a veces´transferir la responsabilidad de las funciones a los objetos hace posible escribirfunciones m´s vers´tiles, y hace m´s f´cil mantener y reutilizar c´digo. a a a a o14.3. Otro ejemploVamos a convertir incremento (de la Secci´n 13.3) en un m´todo. Para aho- o errar espacio, dejaremos a un lado los m´todos ya definidos, pero usted deber´ e ıamantenerlos en su versi´n: oclass Hora: #aqu´ van las definiciones anteriores de m´todos... ı e
    • 152 Clases y m´todos e def incremento(self, segundos): self.segundos = segundos + self.segundos while self.segundos >= 60: self.segundos = self.segundos - 60 self.minutos = self.minutos + 1 while self.minutos >= 60: self.minutos = self.minutos - 60 self.horas = self.horas + 1La transformaci´n es puramente mec´nica; hemos llevado la definici´n del m´to- o a o edo al interior de la definici´n de la clase y hemos cambiado el nombre del primer opar´metro. aAhora podemos invocar incremento como un m´todo. ehoraActual.incremento(500)De nuevo, el objeto sobre el que invocamos el m´todo se asigna al primer par´me- e atro, self. El segundo par´metro, segundos toma el valor de 500. a Como ejercicio, convierta convertirASegundos (de la Secci´n 13.5) o en un m´todo de la clase Hora. e14.4. Un ejemplo m´s complicado aLa funci´n despues es ligeramente m´s complicada porque opera sobre dos o aobjetos Hora, no s´lo sobre uno. S´lo podemos convertir uno de los par´metros o o aen self; el otro se queda como est´: aclass Hora: #aqu´ van las definiciones anteriores de m´todos... ı e def despues(self, hora2): if self.horas > hora2.horas: return 1 if self.horas < hora2.horas: return 0 if self.minutos > hora2.minutos: return 1
    • 14.5 Argumentos opcionales 153 if self.minutos < hora2.minutos: return 0 if self.segundos > hora2.segundos: return 1 return 0Invocamos este m´todo sobre un objeto y pasamos el otro como argumento: eif horaHecho.despues(horaActual): print "El pan estar´ hecho despu´s de empezar." a eCasi puede leer la invocaci´n como una mezcla de ingl´s y espa˜ol: “Si la hora- o e nhecho es depu´s de la hora-actual, entonces...” e14.5. Argumentos opcionalesHemos visto funciones internas que toman un n´mero variable de argumentos. uPor ejemplo, string.find puede tomar dos, tres o cuatro argumentos.Es posible escribir funciones definidas por el usuario con listas de argumentos op-cionales. Por ejemplo, podemos modernizar nuestra propia versi´n de encuentra opara que haga lo mismo que string.find.Esta es la versi´n original de la Secci´n 7.7: o odef encuentra(cad, c): indice = 0 while indice < len(cad): if str[indice] == c: return indice indice = indice + 1 return -1Esta es la versi´n aumentada y mejorada: odef encuentra(cad, c, comienzo=0): indice = comienzo while indice < len(cad): if str[indice] == c: return indice indice = indice + 1 return -1
    • 154 Clases y m´todos eEl tercer par´metro, comienzo, es opcional porque se proporciona un valor por aomisi´n, 0. Si invocamos encuentra s´lo con dos argumentos, utilizamos el valor o opor omisi´n y comenzamos por el principio de la cadena: o>>> encuentra("arriba", "r")1Si le damos un tercer par´metro, anula el predefinido: a>>> encuentra("arriba", "r", 2)2>>> encuentra("arriba", "r", 3)-1 Como ejercicio, a˜ada un cuarto par´metro, fin, que especifique n a d´nde dejar de buscar. o Cuidado: Este ejercicio tiene truco. El valor por omisi´n de fin de- o ber´ ser len(cad), pero eso no funciona. Los valores por omisi´n ıa o se eval´an al definir la funci´n, no al llamarla. Cuando se define u o encuentra, cad a´n no existe, as´ que no puede averiguar su longi- u ı tud.14.6. El m´todo de inicializaci´n e oEl m´todo de inicializaci´n es un m´todo especial que se invoca al crear un e o eobjeto. El nombre de este m´todo es init (dos guiones bajos, seguidos de einit y dos guiones bajos m´s). Un m´todo de inicializaci´n para la clase Hora a e oes as´ ı:class Hora: def __init__(self, horas=0, minutos=0, segundos=0): self.horas = horas self.minutos = minutos self.segundos = segundosNo hay conflicto entre el atributo self.horas y el par´metro horas. la notaci´n a ode punto especifica a qu´ variable nos referimos. eCuando invocamos el constructor Hora, los argumentos que damos se pasan ainit:>>> horaActual = Hora(9, 14, 30)>>> horaActual.imprimeHora()>>> 9:14:30
    • 14.7 Revisi´n de los Puntos o 155Como los par´metros son opcionales, podemos omitirlos: a>>> horaActual = Hora()>>> horaActual.imprimeHora()>>> 0:0:0O dar s´lo el primer par´metro: o a>>> horaActual = Hora (9)>>> horaActual.imprimeHora()>>> 9:0:0O los dos primeros par´metros: a>>> horaActual = Hora (9, 14)>>> horaActual.imprimeHora()>>> 9:14:0Finalmente, podemos dar un subconjunto de los par´metros nombr´ndolos ex- a aplicitamente:>>> horaActual = Hora(segundos = 30, horas = 9)>>> horaActual.imprimeHora()>>> 9:0:3014.7. Revisi´n de los Puntos oVamos a reescribir la clase Punto de la Secci´n 12.1 con un estilo m´s orientado o aa objetos:class Punto: def __init__(self, x=0, y=0): self.x = x self.y = y def __str__(self): return ’(’ + str(self.x) + ’, ’ + str(self.y) + ’)’El m´todo de inicializaci´n toma los valores de x e y como par´metros opcio- e o anales; el valor por omisi´n de cada par´metro es 0. o aEl siguiente m´todo, str , devuelve una representaci´n en forma de cadena e ode un objeto Punto. Si una clase ofrece un m´todo llamado str , se impone eal comportamiento por defecto de la funci´n interna str de Python. o
    • 156 Clases y m´todos e>>> p = Punto(3, 4)>>> str(p)’(3, 4)’Imprimir un objeto Punto invoca impl´ ıcitamente a str sobre el objeto,as´ que definir str tambi´n cambia el comportamiento de print: ı e>>> p = Punto(3, 4)>>> print p(3, 4)Cuando escribimos una nueva clase, casi siempre empezamos escribiendo init , que facilita el instanciar objetos, y str , que casi siempre es util ´para la depuraci´n. o14.8. Sobrecarga de operadoresAlgunos lenguajes hacen posible cambiar la definici´n de los operadores internos ocuando se aplican a tipos definidos por el usuario. Esta caracter´ıstica se llamasobrecarga de operadores. Es especialmente util cuando definimos nuevos ´tipos matem´ticos. aPor ejemplo, para suplantar al operador de suma + necesitamos proporcionarun m´todo llamado add : eclass Punto: # aqu´ van los m´todos que ya hab´amos definido... ı e ı def __add__(self, otro): return Punto(self.x + otro.x, self.y + otro.y)Como es habitual, el primer par´metro es el objeto sobre el que se invoca el am´todo. El segundo par´metro se llama convenientemente otro para distinguirlo e adel mismo (self). Para sumar dos Puntos, creamos y devolvemos un nuevoPunto que contiene la suma de las coordenadas x y la suma de las coordenadasy.Ahora, cuando apliquemos el operador + a objetos Punto, Python invocar´ a a add :>>> p1 = Punto(3, 4)>>> p2 = Punto(5, 7)>>> p3 = p1 + p2>>> print p3(8, 11)
    • 14.8 Sobrecarga de operadores 157La expresi´n p1 + p2 equivale a p1. add (p2), pero es obviamente m´s ele- o agante. Como ejercicio, a˜ada un m´todo sub (self, otro) que sobre- n e cargue el operador resta y pru´belo. eHay varias formas de sobrecargar el comportamiento del operador multiplica-ci´n: definiendo un m´todo llamado mul , o rmul , o ambos. o eSi el operando a la izquierda de * es un Punto, Python invoca a mul , loque presupone que el otro operando es tambi´n un Punto. Calcula el producto einterno de dos puntos, definido seg´n las reglas del ´lgebra lineal: u adef __mul__(self, otro): return self.x * otro.x + self.y * otro.ySi el operando a la izquierda de * es un tipo primitivo y el operando de laderecha es un Punto, Python invca a rmul , lo que realiza una multiplicaci´n oescalar:def __rmul__(self, otro): return Punto(otro * self.x, otro * self.y)El resultado es un nuevo Punto cuyas coordenadas son m´ltiplos de las coorde- unadas originales. Si otro es un tipo que no se puede multiplicar por un n´mero uen coma flotante, entonces rmul causar´ un error. aEste ejemplo muestra ambos tipos de multiplicaci´n: o>>> p1 = Punto(3, 4)>>> p2 = Punto(5, 7)>>> print p1 * p243>>> print 2 * p2(10, 14)¿Qu´ ocurre si intentamos evaluar p2 * 2? Como el primer par´metro es un e aPunto, Python invoca a mul con 2 como el segundo par´metro. Dentro de a mul , el programa intenta acceder a la coordenada x de otro, pero no loconsigue porque un entero no tiene atributos:>>> print p2 * 2AttributeError: ’int’ object has no attribute ’x’Desgraciadamente, el mensaje de error es un poco opaco. Este ejemplo muestraalgunas de las difucultades de la programaci´n orientada a objetos. A veces es odif´ averiguar simplemente qu´ c´digo se est´ ejecutando. ıcil e o a
    • 158 Clases y m´todos ePara ver un ejemplo m´s completo de sobrecarga de operadores, vaya al Ap´ndi- a ece B.14.9. PolimorfismoLa mayor´ de los m´todos que hemos escrito funcionan s´lo para un tipo es- ıa e opec´ıfico. Cuando usted crea un nuevo objeto, escribe m´todos que operan sobre eese tipo.Pero hay ciertas operaciones que querr´ aplicar a muchos tipos, como las opera- aciones aritm´ticas de las secciones anteriores. Si muchos tipos admiten el mismo econjunto de operaciones, puede escribir funciones que trabajen sobre cualquierade esos tipos.Por ejemplo, la operaci´n multisuma (com´n en ´lgebra lineal) toma tres o u apar´metros; multiplica los dos primeros y luego suma el tercero. Podemos escri- abirla en Python as´ ı:def multisuma (x, y, z): return x * y + zEste m´todo trabajar´ con cualquier valor de x e y que se pueda multiplicar y e acon cualquier valor de z que se pueda sumar al producto.Podemos invocarlo con valores num´ricos: e>>> multisuma (3, 2, 1)7O con Puntos:>>> p1 = Punto(3, 4)>>> p2 = Punto(5, 7)>>> print multisuma (2, p1, p2)(11, 15)>>> print multisuma (p1, p2, 1)44En el primer caso, el Punto se multiplica por un escalar y luego se suma aotro Punto. En el segundo caso, el producto interior produce un valor num´rico, eas´ que el tercer par´metro tambi´n debe ser un valor num´rico. ı a e eUna funci´n como ´sta que puede tomar par´metros con diferentes tipos se o e allama polim´rfica. oComo un ejemplo m´s, observe el m´todo delDerechoYDelReves, que imprime a edos veces una lista, hacia adelante y hacia atr´s: a
    • 14.9 Polimorfismo 159def delDerechoYDelReves(derecho): import copy reves = copy.copy(derecho) reves.reverse() print str(derecho) + str(reves)Como el m´todo reverse es un modificador, hacemos una copia de la lista eantes de darle la vuelta. As´ este m´todo no modifica la lista que recibe como ı, epar´metro. aHe aqu´ un ejemplo que aplica delDerechoYDelReves a una lista: ı>>> miLista = [1, 2, 3, 4]>>> delDerechoYDelReves(miLista)[1, 2, 3, 4][4, 3, 2, 1]Por supuesto, pretend´ ıamos aplicar esta funci´n a listas, as´ que no es sorpren- o ıdente que funcione. Lo sorprendente es que pudi´ramos usarla con un Punto. ePara determinar si una funci´n se puede aplicar a un nuevo tipo, aplicamos la oregla fundamental del polimorfismo: Si todas las operaciones realizadas dentro de la funci´n se o pueden aplicar al tipo, la funci´n se puede aplicar al tipo. oLas operaciones del m´todo incluyen copy, reverse y print. ecopy trabaja sobre cualquier objeto, y ya hemos escrito un m´todo str para elos Puntos, as´ que todo lo que necesitamos es un m´todo reverse en la clase ı ePunto:def reverse(self): self.x , self.y = self.y, self.xAhora podemos pasar Puntos a delDerechoYDelReves:>>> p = Punto(3, 4)>>> delDerechoYDelReves(p)(3, 4)(4, 3)El mejor tipo de polimorfismo es el que no se busca, cuando usted descubre queuna funci´n que hab´ escrito se puede aplicar a un tipo para el que nunca la o ıahab´ planeado. ıa
    • 160 Clases y m´todos e14.10. Glosariolenguaje orientado a objetos: Un lenguaje que ofrece caracter´ ısticas, como clases definidas por el usuario y herencia, que facilitan la programaci´no orientada a objetos.programaci´n orientada a objetos: Un estilo de programaci´n en el que los o o datos y las operaciones que los manipulan est´n organizadas en clases y a m´todos. em´todo: Una funci´n definida dentro de una definici´n de clase y que se invoca e o o sobre instancias de esa clase.imponer: Reemplazar una opci´n por omisi´n. Los ejemplos incluyen el reem- o o plazo de un par´metro por omisi´n con un argumento particular y el reem- a o plazo de un m´todo por omisi´n proporcionando un nuevo m´todo con el e o e mismo nombre.m´todo de inicializaci´n: Un m´todo especial que se invoca autom´ticamen- e o e a te al crear un nuevo objeto y que inicializa los atributos del objeto.sobrecarga de operadores: Ampliar los operadores internos (+, -, *, >, <, etc.) de modo que trabajen con tipos definidos por el usuario.producto interno: Una operaci´n definida en ´lgebra lineal que multiplica o a dos Puntos y entrega un valor num´rico. emultiplicaci´n escalar: Una operaci´n definida en ´lgebra lineal que multi- o o a plica cada una de las coordenadas de un Punto por un valor num´rico. epolim´rfica: Una funci´n que puede operar sobra m´s de un tipo. Si todas las o o a operaciones realizadas dentro de una funci´n se pueden aplicar a un tipo, o la funci´n se puede aplicar a ese tipo. o
    • Cap´ ıtulo 15Conjuntos de objetos15.1. Composici´n oHasta ahora, ya ha visto varios ejemplos de composici´n. Uno de los primeros oejemplos fue el uso de la llamada a un m´todo como parte de una expresi´n. e oOtro ejemplo es la estructura anidada de las sentencias; se puede escribir unasentencia if dentro de un bucle while, dentro de otra sentencia if, y as´ suce- ısivamente.Una vez visto este patr´n, y sabiendo acerca de listas y objetos, no le deber´ o ıasorprender que pueda crear listas de objetos. Tambi´n puede crear objetos que econtengan listas (en forma de atributos); puede crear listas que contengan listas;objetos que contengan objetos, y as´ indefinidamente. ıEn este cap´ıtulo y el siguiente, exploraremos algunos ejemplos de estas combi-naciones, y usaremos objetos Carta como ejemplo.15.2. Objetos CartaSi no est´ usted familiarizado con los naipes de juego comunes, puede ser un abuen momento para que consiga un mazo, si no este cap´ ıtulo puede que no tengamucho sentido. Hay cincuenta y dos naipes en una baraja inglesa, cada uno delos cuales pertenece a un palo y tiene un valor; hay cuatro palos diferentes ytrece valores. Los palos son Picas, Corazones, Diamantes, y Tr´boles (en el orden edescendente seg´n el bridge). Los valores son As, 2, 3, 4, 5, 6, 7, 8, 9, 10, Sota, uReina, y Rey. Dependiendo del tipo de juego que se juegue, el valor del As puedeser mayor al Rey o inferior al 2.
    • 162 Conjuntos de objetosSi queremos definir un nuevo objeto para representar un naipe, es obvio qu´ atri- ebutos deber´ tener: valor y palo. Lo que no es tan obvio es el tipo que se debe ıadar a los atributos. Una posibilidad es usar cadenas de caracteres que contenganpalabras como "Picas" para los palos y "Reina" para los valores. Un problemade esta implementaci´n es que no ser´ f´cil comparar naipes para ver cu´l tiene o a a amayor valor o palo.Una alternativa es usar n´meros enteros para codificar los valores y palos. Con uel t´rmino “codificar” no queremos significar lo que algunas personas pueden epensar, acerca de cifrar o traducir a un c´digo secreto. Lo que un programador oentiende por “codificar” es “definir una correspondencia entre una secuencia den´meros y los elementos que se desea representar”. Por ejemplo: u Picas → 3 Corazones → 2 Diamantes → 1 Tr´boles e → 0Esta correspondencia tiene una caracter´ ıstica obvia: los palos corresponden an´meros enteros en orden, o sea que podemos comparar los palos al comparar ulos n´meros. La asociaci´n de los valores es bastante obvia; cada uno de los u ovalores num´ricos se asocia con el entero correspondiente, y para las figuras: e Sota → 11 Reina → 12 Rey → 13Estamos usando una notaci´n matem´tica para estas asociaciones por una o araz´n: no son parte del programa Python. Son parte del dise˜o del progra- o nma, pero nunca aparecen expl´ ıcitamente en el c´digo fuente. La definici´n de o oclase para el tipo Carta se parecer´ a: aclass Carta: def __init__(self, palo=0, valor=0): self.palo = palo self.valor = valorComo acostumbramos, proporcionaremos un m´todo de inicializaci´n que toma e oun par´metro opcional para cada atributo. aPara crear un objeto que representa el 3 de Tr´boles, usaremos la instrucci´n: e otresDeTreboles = Carta(0, 3)El primer argumento, 0, representa el palo de Tr´boles. e
    • 15.3 Atributos de clase y el m´todo e str 16315.3. Atributos de clase y el m´todo e strPara poder imprimir los objetos Carta de una manera f´cil de leer para las apersonas, vamos a establecer una correspondencia entre los c´digos enteros y olas palabras. Una manera natural de hacer esto es con listas de cadenas decaracteres. Asignaremos estas listas dentro de atributos de clase al principiode la definici´n de clase: oclass Carta: listaDePalos = ["Tr´boles", "Diamantes", "Corazones", e "Picas"] listaDeValores = ["nada", "As", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Sota", "Reina", "Rey"] # se omite el m´todo init e def __str__(self): return (self.listaDeValores[self.valor] + " de " + self.listaDePalos[self.palo])Un atributo de clase se define fuera de cualquier m´todo, y puede accederse edesde cualquiera de los m´todos de la clase. eDentro de str , podemos usar listaDePalos y listaDeValores para aso-ciar los valores num´ricos de palo y valor con cadenas de caracteres. Por eejemplo, la expresi´n self.listaDePalos[self.palo] significa “usa el atribu- oto palo del objeto self como un ´ ındice dentro del atributo de clase denominadolistaDePalos, y selecciona la cadena apropiada”.El motivo del ‘‘nada" en el primer elemento de listaDeValores es para rellenodel elemento de posici´n cero en la lista, que nunca se usar´. Los unicos valores o a ´l´ ıcitos para el valor van de 1 a 13. No es obligatorio que desperdiciemos esteprimer elemento. Podr´ ıamos haber comenzado en 0 como es usual, pero es menosconfuso si el 2 se codifica como 2, el 3 como 3, y as´ sucesivamente. ıCon los m´todos que tenemos hasta ahora, podemos crear e imprimir naipes: e>>> carta1 = Carta(1, 11)>>> print carta1Sota de DiamantesLos atributos de clase como listaDePalos son compartidos por todos los ob-jetos de tipo Carta. La ventaja de esto es que podemos usar cualquier objetoCarta para acceder a los atributos de clase:
    • 164 Conjuntos de objetos>>> carta2 = Carta(1, 3)>>> print carta23 de Diamantes>>> print carta2.listaDePalos[1]DiamantesLa desventaja es que si modificamos un atributo de clase, afectaremos a cadainstancia de la clase. Por ejemplo, si decidimos que “Sota de Diamantes” enrealidad deber´ llamarse “Sota de Ballenas Bailarinas”, podr´ ıa ıamos hacer losiguiente:>>> carta1.listaDePalos[1] = "Ballenas Bailarinas">>> print carta1Sota de Ballenas BailarinasEl problema es que todos los Diamantes se transformar´n en Ballenas Bailarinas: a>>> print carta23 de Ballenas BailarinasEn general no es una buena idea modificar los atributos de clase.15.4. Comparaci´n de naipes oPara los tipos primitivos, existen operadores condicionales (< , >, ==, etc.) quecomparan valores y determinan cuando uno es mayor, menor, o igual a otro.Para los tipos definidos por el usuario, podemos sustituir el comportamientode los operadores internos si proporcionamos un m´todo llamado cmp . Por econvenci´n, cmp toma dos par´metros, self y otro, y retorna 1 si el primer o aobjeto es el mayor, -1 si el segundo objeto es el mayor, y 0 si ambos son iguales.Algunos tipos est´n completamente ordenados, lo que significa que se pueden acomparar dos elementos cualesquiera y decir cu´l es el mayor. Por ejemplo, los an´meros enteros y los n´meros en coma flotante tienen un orden completo. Algu- u unos conjuntos no tienen orden, o sea, que no existe ninguna manera significativade decir que un elemento es mayor a otro. Por ejemplo, las frutas no tienenorden, lo que explica por qu´ no se pueden comparar peras con manzanas. eEl conjunto de los naipes tiene un orden parcial, lo que significa que algunasveces se pueden comparar los naipes, y otras veces no. Por ejemplo, usted sabeque el 3 de Tr´boles es mayor que el 2 de Tr´boles y el 3 de Diamantes es e emayor que el 3 de Tr´boles. Pero, ¿cu´l es mejor?, ¿el 3 de Tr´boles o el 2 de e a eDiamantes?. Uno tiene mayor valor, pero el otro tiene mayor palo.
    • 15.5 Mazos de naipes 165A los fines de hacer que los naipes sean comparables, se debe decidir qu´ es m´s e aimportante: valor o palo. Para no mentir, la selecci´n es arbitraria. Como algo ohay que elegir, diremos que el palo es m´s importante, porque un mazo nuevo aviene ordenado con todos los Tr´boles primero, luego con todos los Diamantes, ey as´ sucesivamente. ıCon esa decisi´n tomada, podemos escribir o cmp :def __cmp__(self, otro): # controlar el palo if self.palo > otro.palo: return 1 if self.palo < otro.palo: return -1 # si son del mismo palo, controlar el valor if self.valor > otro.valor: return 1 if self.valor < otro.valor: return -1 # los valores son iguales, es un empate return 0En este ordenamiento, los Ases son menores que los doses. Como ejercicio, modifique cmp de tal manera que los Ases tengan mayor valor que los Reyes.15.5. Mazos de naipesAhora que ya tenemos los objetos para representar las Cartas, el pr´ximo paso ol´gico es definir una clase para representar un Mazo. Por supuesto, un mazo oest´ compuesto de naipes, as´ que cada objeto Mazo contendr´ una lista de a ı anaipes como atributo.A continuaci´n se muestra una definici´n para la clase Mazo. El m´todo de o o einicializaci´n crea el atributo cartas y genera el conjunto est´ndar de cincuenta o ay dos naipes.class Mazo: def __init__(self): self.cartas = [] for palo in range(4): for valor in range(1, 14): self.cartas.append(Carta(palo, valor))La forma m´s f´cil de poblar el mazo es mediante un bucle anidado. El bucle a aexterior enumera los palos desde 0 hasta 3. El bucle interior enumera los va-lores desde 1 hasta 13. Como el bucle exterior itera cuatro veces, y el interior
    • 166 Conjuntos de objetositera trece veces, la cantidad total de veces que se ejecuta el cuerpo interior escincuenta y dos (trece por cuatro). Cada iteraci´n crea una nueva instancia de oCarta con el palo y valor actual, y agrega dicho naipe a la lista de cartas.El m´todo append funciona sobre listas pero no sobre tuplas, por supuesto. e15.6. Impresi´n del mazo de naipes oComo es usual, cuando definimos un nuevo tipo de objeto queremos un m´todo eque imprima el contenido del objeto. Para imprimir un Mazo, recorremos la listae imprimimos cada Carta:class Mazo: ... def muestraMazo(self): for carta in self.cartas: print cartaDesde ahora en adelante, los puntos suspensivos (...) indicar´n que hemos aomitido los otros m´todos en la clase. eEn lugar de escribir un m´todo muestraMazo, podr´ e ıamos escribir un m´todo e str para la clase Mazo. La ventaja de str est´ en que es m´s flexible. En a alugar de imprimir directamente el contenido del objeto, str genera una re-presentaci´n en forma de cadena de caracteres que las otras partes del programa opueden manipular antes de imprimir o almacenar para un uso posterior.Se presenta ahora una versi´n de str que retorna una representaci´n como o ocadena de caracteres de un Mazo. Para darle un toque especial, acomoda losnaipes en una cascada, de tal manera que cada naipe est´ sangrado un espacio am´s que el precedente. aclass Mazo: ... def __str__(self): s = "" for i in range(len(self.cartas)): s = s + " "*i + str(self.cartas[i]) + "n" return sEste ejemplo demuestra varias caracter´ ısticas. Primero, en lugar de recorrerself.cartas y asignar cada naipe a una variable, usamos i como variable debucle e ´ ındice de la lista de naipes.Segundo, utilizamos el operador de multiplicaci´n de cadenas de caracteres para osangrar cada naipe un espacio m´s que el anterior. La expresi´n *i prooprciona a ouna cantidad de espacios igual al valor actual de i.
    • 15.7 Barajar el mazo 167Tercero, en lugar de usar la instrucci´n print para imprimir los naipes, utiliza- omos la funci´n str. El pasar un objeto como argumento a str es equivalente a oinvocar el m´todo str sobre dicho objeto. eFinalmente, usamos la variable s como acumulador. Inicialmente, s es unacadena de caracteres vac´ En cada pasada a trav´s del bucle, se genera una ıa. enueva cadena de caracteres que se concatena con el viejo valor de s para obtenerel nuevo valor. Cuando el bucle termina, s contiene la representaci´n completa oen formato de cadena de caracteres del Mazo, la cual se ve como a continuaci´nose presenta:>>> mazo = Mazo()>>> print mazoAs de Tr´boles e 2 de Tr´boles e 3 de Tr´boles e 4 de Tr´boles e 5 de Tr´boles e 6 de Tr´boles e 7 de Tr´boles e 8 de Tr´bolese 9 de Tr´boles e 10 de Tr´bolese Sota de Tr´boles e Reina de Tr´boles e Rey de Tr´boles e As of DiamantesY as´ sucesivamente. A´n cuando los resultados aparecen en 52 renglones, se ı utrata de s´lo una unica larga cadena de caracteres que contiene los saltos de o ´l´ ınea.15.7. Barajar el mazoSi un mazo est´ perfectamente barajado, cualquier naipe tiene la misma proba- abilidad de aparecer en cualquier posici´n del mazo, y cualquier lugar en el mazo otiene la misma probabilidad de contener cualquier naipe.Para mezclar el mazo, utilizaremos la funci´n randrange del m´dulo random. Es- o ota funci´n toma dos enteros como argumentos a y b, y elige un n´mero entero en o u
    • 168 Conjuntos de objetosforma aleatoria en el rango a <= x <b. Como el l´ ımite superior es estrictamen-te menor a b, podemos usar la longitud de la lista como el segundo argumentoy de esa manera tendremos garantizado un ´ ındice legal dentro de la lista. Porejemplo, esta expresi´n selecciona el ´ o ındice de un naipe al azar dentro del mazo:random.randrange(0, len(self.cartas))Una manera sencilla de mezclar el mazo es recorrer los naipes e intercambiarcada una con otra elegida al azar. Es posible que el naipe se intercambie consigomismo, pero no es un problema. De hecho, si eliminamos esa posibilidad, elorden de los naipes no ser´ completamente al azar: aclass Mazo: ... def mezclar(self): import random nCartas = len(self.cartas) for i in range(nCartas): j = random.randrange(i, nCartas) self.cartas[i], self.cartas[j] = self.cartas[j], self.cartas[i]En lugar de presuponer que hay cincuenta y dos naipes en el mazo, obtenemosla longitud real de la lista y la almacenamos en nCartas.Para cada naipe del mazo, seleccionamos un naipe al azar entre aquellos que nohan sido intercambiados a´n. Luego intercambiamos el naipe actual (i) con el unaipe seleccionado (j). Para intercambiar los naipes usaremos la asignaci´n de otuplas, como se describe en la Secci´n 9.2: oself.cartas[i], self.cartas[j] = self.cartas[j], self.cartas[i] Como ejercicio, reescriba esta l´ ınea de c´digo sin usar una asigna- o ci´n de secuencias. o15.8. Eliminaci´n y reparto de los naipes oOtro m´todo que podr´ ser util para la clase Mazo es eliminaCarta, que toma e ıa ´un naipe como par´metro, lo elimina, y retorna verdadero (1) si el naipe estaba aen el mazo, y falso (0) si no estaba:
    • 15.9 Glosario 169class Mazo: ... def eliminaCarta(self, carta): if carta in self.cartas: self.cartas.remove(carta) return 1 else: return 0El operador in retorna verdadero si el primer operando est´ en el segundo, el acual debe ser una lista o tupla. Si el primer operando es un objeto, Python usael m´todo cmp del objeto para determinar la igualdad entre los elementos de ela lista. Como el cmp en la clase Carta verifica la igualdad en profundidad,el m´todo eliminaCarta tambi´n verifica igualdad en profundidad. e ePara repartir los naipes, queremos eliminar y devolver el naipe que ocupa laposici´n superior en el mazo. El m´todo pop de las listas proporciona una manera o econveniente de realizar esto:class Mazo: ... def darCarta(self): return self.cartas.pop()En realidad, pop elimina el ultimo naipe en la lista, as´ que en efecto estamos ´ ırepartiendo desde el extremo inferior del mazo.Otra operaci´n m´s que es muy probable necesitemos es la funci´n booleana o a oestaVacio, la cual devuelve verdadero si el mazo no contiene ning´n naipe: uclass Deck: ... def estaVacio(self): return (len(self.cartas) == 0)15.9. Glosariocodificar: Representar un conjunto de valores uilizando otro conjunto de valo- res, entre los cuales se construye una correspondencia.atributo de clase: Una variable que se define dentro de la definici´n de una o clase pero fuera de cualquiera de sus m´todos. Los atributos de clase son e accesibles desde cualquier m´todo de la clase y est´n compartidos por e a todas las instancias de la misma.
    • 170 Conjuntos de objetosacumulador: Una variable que se usa en un bucle para acumular una serie de valores, por ejemplo concaten´ndolos dentro de una cadena de caracteres a o adicion´ndolos a una suma. a
    • Cap´ ıtulo 16Herencia16.1. HerenciaLa caracter´ ıstica de un lenguaje que m´s se asocia con la programaci´n orientada a oa objetos es la herencia. La herencia es la capacidad de definir una nueva claseque es una versi´n modificada de otra ya existente. oLa principal ventaja de esta caracter´ıstica es que se pueden agregar nuevos m´to- edos a una clase sin modificar la clase existente. Se denomina “herencia” porquela nueva clase hereda todos los m´todos de la clase existente. Si extendemos eesta mat´fora, a la clase existente a veces se la denomina clase padre. La nueva eclase puede denominarse clase hija, o tambi´n “subclase”. eLa herencia es una caracter´ıstica poderosa. Ciertos programas que ser´ compli- ıancados sin herencia pueden escribirse de manera simple y concisa gracias a ella.Adem´s, la herencia puede facilitar la reutilizaci´n del c´digo, pues se puede a o oadaptar el comportamiento de la clase padre sin tener que modificarla. En algu-nos casos, la estructura de la herencia refleja la propia estructura del problema,lo que hace que el programa sea m´s f´cil de comprender. a aPor otro lado, la herencia pude hacer que los porgramas sean dif´ ıciles de leer.Cuando se llama a un m´todo, a veces no est´ claro d´nde debe uno encontrar e a osu definici´n. El c´digo relevante puede estar diseminado por varios m´dulos. o o oAdem´s, muchas de las cosas que se hacen mediante el uso de la herencia, se pue- aden lograr de forma igualmente (incluso m´s) elegante sin ella. Si la estructura ageneral del problema no nos gu´ hacia la herencia, dicho estilo de programaci´n ıa opuede hacer m´s mal que bien. a
    • 172 HerenciaEn este cap´ ıtulo demostraremos el uso de la herencia como parte de un programaque juega a las cartas a la “Mona”. Una de nuestras metas ser´ que el c´digo a oque escribamos se pueda reutilizar para implementar otros juegos de naipes.16.2. Una mano de cartasPara casi cualquier juego de naipes, necesitamos representar una mano de cartas.Una mano es similar a un mazo, por supuesto. Ambos est´n compuestos de un aconjunto de naipes, y ambos requieren de operaciones tales como agregar yeliminar una carta. Adem´s, necesitaremos la capacidad de mezclar tanto un amazo como una mano de cartas.Una mano es diferente de un mazo en ciertos aspectos. Seg´n el juego al que se uest´ jugando, podemos querer realizar ciertas operaciones sobre una mano que eno tienen sentido sobre un mazo. Por ejemplo, en el p´ker queremos clasificar ouna mano (straight (consecutiva), flush (de un solo palo), etc.) y compararla conotra. En bridge necesitaremos calcular el puntaje para la mano para as´ poder ıhacer la subasta.Esta situaci´n sugiere el uso de la herencia. Si Mano es una subclase de Mazo, oentonces tendr´ todos los m´todos de Mazo y le podremos agregar otros m´todos a e enuevos.En la definici´n de clase, el nombre de la clase padre aparece entre par´ntesis: o eclass Mano(Mazo): passEsta sentencia indica que la nueva clase Mano hereda de la clase existente Mazo.El constructor de Mano inicializa los atributos para la mano, que son nombre ycartas. La cadena de caracteres nombre identifica a esta mano, probablementemediante el nombre del jugador que la sostiene. El nombre es un par´metro aopcional con un valor por omisi´n de cadena vac´ cartas es la lista de cartas o ıa.de la mano, inicializada como lista vac´ıa.class Mano(Mazo): def __init__(self, nombre=""): self.cartas = [] self.nombre = nombreCasi para cualquier juego de naipes, es necesario agregar y quitar cartas del ma-zo. La eliminaci´n de cartas ya ha sido resuelta, pues Mano hereda eliminaCarta ode Mazo. Pero deberemos escribir agregaCarta:
    • 16.3 El reparto de los naipes 173class Mano(Mazo): ... def agregaCarta(self,carta) : self.cartas.append(carta)De nuevo, los puntos suspensivos indican que hemos omitido los otrs m´todos. eEl m´todo de lista append agrega la nueva carta al final de la lista de cartas. e16.3. El reparto de los naipesAhora que ya tenemos la clase Mano, queremos repartir las cartas del Mazo enmanos. No es claramente obvio si este m´todo debe ir en la clase Mano o en la eclase Mazo, pero como opera sobre un mazo unico y (posiblemente) sobre varias ´manos, es m´s natural ponerlo en el Mazo. arepartir debe ser bastante general, pues los diferentes juegos tienen distintosrequerimentos. Puede que necesitemos repartir todo el mazo de una vez, o queagreguemos una carta a cada mano.repartir toma dos par´metros, una lista (o tupla) de manos y la cantidad total ade naipes a repartir. Si no hay suficientes cartas en el mazo, el m´todo reparte etodas las cartas y se detiene:class Mazo : ... def repartir(self, manos, nCartas=999): nManos = len(manos) for i in range(nCartas): if self.estaVacio(): break # fin si se acaban las cartas carta = self.darCarta() # da la carta superior mano = manos[i % nManos] # a qui´n le toca? e mano.agregaCarta(carta) # agrega la carta a la manoEl segundo par´metro, nCartas es opcional; el valor por omisi´n es un n´mero a o umuy grande, lo cual es lo mismo que decir que se repartir´n todos los naipes del amazo.La variable de bucle i va desde 0 hasta nCartas-1. A cada paso a trav´s del ebucle, se elimina una carta del mazo mediante el m´todo de lista pop, que quita ey devuelve el ultimo elemento de la lista. ´El operador m´dulo ( %) permite que podamos repartir las cartas de una en una o(una carta cada vez para cada mano). Cuando i es igual a la cantidad de manosen la lista, la expresi´n i % nManos salta hacia el comienzo de la lista (el ´ o ındicees 0).
    • 174 Herencia16.4. Mostremos la manoPara mostrar el contenido de una mano, podemos sacar partido de la existenciade los m´todos muestraMazo y str que se heredan de Mazo. Por ejemplo: e>>> mazo = Mazo()>>> mazo.mezclar()>>> mano = Mano("hugo")>>> mazo.repartir([mano], 5)>>> print manoLa mano de hugo contiene2 de Picas 3 de Picas 4 de Picas As de Corazones 9 de Tr´boles eNo es una gran mano, pero tiene lo necesario como para disponer de una escalerade color.Aunque es conveniente usar la herencia de los m´todos existentes, existe infor- emaci´n adicional en una Mano que desear´ o ıamos mostrar al imprimirla. Para ello,podemos proporcionar a la clase Mano un m´todo str que reemplace al de ela clase Mazo:class Mano(Mazo) ... def __str__(self): s = "La mano de " + self.nombre if self.estaVacio(): s = s + " est´ vac´an" a ı else: s = s + " contienen" return s + Mazo.__str__(self)Al principio s es una cadena de caracteres que identifica a la mano. Si la manoest´ vac´ el programa agrega las palabras est´ vac´a y devuelve s. a ıa, a ıEn caso contrario, el programa agrega la palabra contiene y la representaci´n ocomo cadena de caracteres del Mazo, que se obtiene llamando al m´todo str ede la clase Mazo sobre la instancia self.Puede parecer extra˜o que enviemos a self, que se refiere a la Mano actual, ncomo argumento de un m´todo de la clase Mazo, hasta que nos damos cuenta ede que una Mano es un tipo de Mazo. Los objetos Mano pueden hacer cualquier
    • 16.5 La clase JuegoDeCartas 175cosa que pueda hacer un objeto Mazo, y por ello es legal que pasemos una Manoa un m´todo de Mazo. eEn general, siempre es legal usar una instancia de una subclase en el lugar deuna instancia de una clase padre.16.5. La clase JuegoDeCartasLa clase JuegoDeCartas asume la responsabilidad sobre algunas obligacionesb´sicas comunes a todos los juegos, tales como la creaci´n del mazo y la mezcla a ode los naipes:class JuegoDeCartas: def __init__(self): self.mazo = Mazo() self.mazo.mezclar()Esta es la primera vez que vemos que un m´todo de inicializaci´n realiza una e oactividad computacional significativa, m´s all´ de la inicializaci´n de atributos. a a oPara implementar juegos espec´ ıficos, debemos heredar de JuegoDeCartas yagregar las caracter´ ısticas del nuevo juego. Como ejemplo, escribiremos unasimulaci´n para La Mona. oLa meta de La Mona es desembarazarse de las cartas que uno tiene en la mano.Uno se saca las cartas de encima emparej´ndolas por valor y color. Por ejemplo, ael 4 de Tr´boles se empareja con el 4 de Picas porque ambos palos son negros. eLa Sota de Corazones se empareja con la Sota de Diamantes porque ambos sonrojos.Para iniciar el juego, se elimina la Reina de Tr´boles del mazo, de manera que ela Reina de Picas no tiene con qui´n emparejarse. Las cincuenta y una cartas erestantes se reparten entre los jugadores, de una en una. Luego del reparto,todos los jugadores emparejan y descartan tantas cartas como sea posible.Cuando no se pueden realizar m´s concordancias, el juego comienza. Por turnos, acada jugador toma una carta (sin mirarla) del vecino m´s cercano de la izquierda aque a´n tiene cartas. Si la carta elegida concuerda con una de la mano del ujugador, se elimina dicho par. Si no, la carta se agrega a la mano del jugador.Llega el momento en el que se realizan todas las concordancias posibles, con loque queda s´lo la Reina de Picas en la mano del perdedor. oEn nuestra simulaci´n inform´tica del juego, la computadora juega todas las o amanos. Desafortunadamente, se pierden algunos de los matices del juego real.En una partida real, el jugador que tiene la Mona realiza ciertos esfuerzos para
    • 176 Herenciaque su vecino la tome, por ejemplo mostr´ndola prominentemente o al contrario, aerrando al intentar mostrarla abiertamente, o incluso puede fallar al tratar deerrar en su intento de mostrarla prominentemente. La computadora simplementetoma una carta al azar de su vecino.16.6. La clase ManoDeLaMonaUna mano para jugar a La Mona requiere ciertas capacidades que est´n m´s a aall´ de las que posee una Mano. Definiremos una nueva clase ManoDeLaMona, aque hereda de Mano y nos proporciona un m´todo adicional denominado eeliminaCoincidencias:class ManoDeLaMona(Mano): def eliminaCoincidencias(self): cant = 0 cartasOriginales = self.cartas[:] for carta in cartasOriginales: empareja = Carta(3 - carta.palo, carta.valor) if empareja in self.cartas: self.cartas.remove(carta) self.cartas.remove(empareja) print "Mano %s: %s con %s" % (self.nombre,carta,empareja) cant = cant + 1 return cantComenzamos por hacer una copia de la lista de las cartas, de tal manera quepodamos recorrer la copia mientras vamos quitando cartas de la lista original.Como self.cartas se modifica en el bucle, no vamos a querer usarla paracontrolar el recorrido. ¡Python puede quedar realmente confundido si se recorreuna lista que est´ cambiando! aPara cada carta de la mano, averiguamos cu´l es la carta que concordar´ con a aella y la buscamos. La carta que concuerda tiene el mismo valor y el otro palodel mismo color. La expresi´n 3 - carta.palo transforma un Tr´bol (palo 0) o een una Pica (palo 3) y un Diamante (palo 1) en un Coraz´n (palo 2). Verifique opor su cuenta que las operaciones opuestas tambi´n funcionan. Si la carta que econcuerda est´ en la mano, ambas se eliminan. aEl siguiente ejemplo demuestra el uso de eliminaCoincidencias:>>> juego = JuegoDeCartas()>>> mano = ManoDeLaMona("hugo")>>> juego.mazo.repartir([mano], 13)
    • 16.7 La clase JuegoDeLaMona 177>>> print manoLa mano de hugo contieneAs de Picas 2 de Diamantes 7 de Picas 8 de Tr´boles e 6 de Corazones 8 de Picas 7 de Tr´boles e Raina de Tr´boles e 7 de Diamantes 5 de Tr´boles e Sota de Diamantes 10 de Diamantes 10 de Corazones>>> mano.eliminaCoincidencias()Mano hugo: 7 de Picas con 7 de Tr´boles eMano hugo: 8 de Picas con 8 de Tr´boles eMano hugo: 10 de Diamantes con 10 de CorazonesDebe usted notar que no existe un m´todo e init para la clase ManoDeLaMona.Lo heredamos de Mano.16.7. La clase JuegoDeLaMonaAhora podemos poner nuestra atenci´n en el juego en s´ mismo. JuegoDeLaMona o ıes una subclase de JuegoDeCartas con un m´todo nuevo denominado jugar que etoma una lista de jugadores como par´metro. aComo el m´todo init se hereda de JuegoDeCartas, el nuevo objeto eJuegoDeLaMona contiene un mazo recientemtente mezclado:class JuegoDeLaMona(JuegoDeCartas): def jugar(self, nombres): # quitamos la Reina de Tr´boles e self.mazo.eliminaCarta(Carta(0,12)) # construimos una mano para cada jugador self.manos = [] for nombre in nombres : self.manos.append(ManoDeLaMona(nombre))
    • 178 Herencia # repartimos los naipes self.mazo.repartir(self.manos) print "----- Se han repartido las cartas." self.muestraManos() # eliminamos las coincidencias iniciales emparejadas = self.eliminaTodasLasCoincidencias() print "----- Coincidencias eliminadas, el juego comienza." self.muestraManos() # se juega hasta que se han descartado las 50 cartas turno = 0 cantManos = len(self.manos) while emparejadas < 25: emparejadas = emparejadas + self.jugarUnTurno(turno) turno = (turno + 1) % cantManos print "----- El juego termin´." o self.muestraManos()Algunos de los pasos que componen el juego se han colocado en m´todos se- eparados. eliminaTodasLasCoincidencias recorre la lista de manos y llama aeliminaCoincidencias para cada una de ellas:class JuegoDeLaMona(JuegoDeCartas): ... def eliminaTodasLasCoincidencias(self): cant = 0 for mano in self.manos: cant = cant + mano.eliminaCoincidencias() return cant Como ejercicio, escriba muestraManos, el cual recorre self.manos y muestra cada mano.cant es un acumulador que va sumando la cantidad de concordancias en cadamano y devuelve el total.Cuando la cantidad total de coincidencias alcanza a las veinticinco significa quese han eliminado cincuenta cartas de las manos, lo que es lo mismo que decirque s´lo queda una carta y el juego ha terminado. oLa variable turno recuerda el turno de cu´l jugador se est´ jugando. Comienza a aen cero y se incrementa en uno cada vez; cuando alcanza el valor cantManos, eloperador de m´dulo lo hace volver a cero. o
    • 16.7 La clase JuegoDeLaMona 179El m´todo jugarUnTurno toma un par´metro que indica de qui´n es el turno. e a eEl valor de retorno es la cantidad de concordancias que se han realizado duranteese turno:class JuegoDeLaMona(JuegoDeCartas): ... def jugarUnTurno(self, i): if self.manos[i].estaVacio(): return 0 vecino = self.encuentraVecino(i) cartaElegida = self.manos[vecino].darCarta() self.manos[i].agregaCarta(cartaElegida) print "Mano", self.manos[i].nombre, "eligi´", cartaElegida o cant = self.manos[i].eliminaCoincidencias() self.manos[i].mezclar() return cantSi la mano de un jugador est´ vac´ el jugador sali´ del juego, as´ que no hace a ıa, o ınada y devuelve 0.Si no, un turno consiste en encontrar el primer jugador a la izquierda que a´nutiene cartas, tomar una carta de las que posee, y controlar si hay concordancias.Antes de volver se mezclan las cartas de la mano, de tal manera que la selecci´n odel siguiente jugador sea al azar.El m´todo encuentraVecino comienza con el jugador que est´ inmediatamante e aa la izquierda y contin´a alrededor del c´ u ırculo hasta que encuentra un jugadorque a´n tiene cartas. uclass JuegoDeLaMona(JuegoDeCartas): ... def encuentraVecino(self, i): cantManos = len(self.manos) for proximo in range(1,cantManos): vecino = (i + proximo) % cantManos if not self.manos[vecino].estaVacio(): return vecinoSi por cualquier motivo encuentraVecino llegara a dar la vuelta completa alc´ ırculo sin encontrar cartas, devolver´ None y eso causar´ un error en alguna ıa ıaotra parte del programa. Afortunadamante podemos probar que eso no va asuceder nunca (siempre y cuando se detecte correctamente el final del juego). ´Hemos omitido el m´todo muestraManos. Ese puede escribirlo usted mismo. eLa siguiente salida proviene de una forma reducida del juego, en la cual solamen-te se reparten las quince cartas m´s altas (desde los dieces hacia arriba) a tres a
    • 180 Herenciajugadores. Con este mazo m´s peque˜o, el juego termina tras siete coincidencias, a nen lugar de veinticinco.>>> import cartas>>> juego = cartas.JuegoDeLaMona()>>> juego.jugar(["Allen","Jeff","Chris"])----- Se han repartido las cartas.Mano Allen contieneRey de Corazones Sota de Tr´boles e Reina de Picas Rey de Picas 10 de DiamantesMano Jeff contieneReina de Corazones Sota de Picas Sota de Corazones Rey de Diamantes Reina de DiamantesMano Chris contieneSota de Diamantes Rey de Tr´boles e 10 de Picas 10 de Corazones 10 de Tr´boles eMano Jeff: Reina de Corazones con Reina de DiamantesMano Chris: 10 de Picas con 10 de Tr´boles e----- Se eliminaron las coincidencias, el juego comienza.Mano Allen contieneRey de Corazones Sota de Tr´boles e Reina de Picas Rey de Picas 10 de DiamantesMano Jeff contieneSota de Picas Sota de Corazones Rey de Diamantes
    • 16.8 Glosario 181Mano Chris contieneSota de Diamantes Rey de Tr´boles e 10 de CorazonesMano Allen: eligi´ Rey de Diamantes oMano Allen: Rey de Corazones con Rey de DiamantesMano Jeff: eligi´ 10 de Corazones oMano Chris: eligi´ Sota de Tr´boles o eMano Allen: eligi´ Sota de Corazones oMano Jeff: eligi´ Sota de Diamantes oMano Chris: eligi´ Reina de Picas oMano Allen: eligi´ Sota de Diamantes oMano Allen: Sota de Corazones con Sota de DiamantesMano Jeff: eligi´ Rey de Tr´boles o eMano Chris: eligi´ Rey de Picas oMano Allen: eligi´ 10 de Corazones oMano Allen: 10 de Diamantes con 10 de CorazonesMano Jeff: eligi´ Reina de Picas oMano Chris: eligi´ Sota de Picas oMano Chris: Sota de Tr´boles con Sota de Picas eMano Jeff: eligi´ Rey de Picas oMano Jeff: Rey de Tr´boles con Rey de Picas e----- El juego termin´. oLa mano de Allen est´ vac´a. a ıLa mano de Jeff contieneReina de PicasLa mano de Chris est´ vac´a. a ıAs´ que Jeff es quien perdi´. ı o16.8. Glosarioherencia: La capacidad de definir una nueva clase que es una versi´n modifi- o cada de una clase previamente definida.clase padre: Aquella clase de la cual la clase hija hereda.
    • 182 Herenciaclase hija: Una nueva clase creada heredando de una clase existente; tambi´n e se la llama “subclase”.
    • Cap´ ıtulo 17Listas enlazadas17.1. Referencias incrustadasHemos visto ejemplos de atributos que hacen referencia a otros objetos, a losque llamamos referencias incrustadas (v´ase la Secci´n 12.8). Una estrutura e ode datos com´n, la lista enlazada, saca partido de esta caracter´ u ıstica.Las listas enlazadas se componen de nodos, donde cada nodo contiene unareferencia al pr´ximo nodo de la lista. Adem´s, cada nodo contiene una unidad o ade datos llamada cargaPodemos considerar una lista enlazada como una estructura de datos recur-siva porque tiene una definici´n recursiva. o Una lista enlazada puede ser: la lista vac´ representada por None, o bien ıa, un nodo que contiene un objeto de carga y una referencia a una lista enlazada.Las estructuras recursivas de datos nos llevan a m´todos recursivos. e17.2. La clase NodoComo es habitual cuando se escribe una clase, comenzaremos con los m´todos ede inicializaci´n y str , para poder comprobar el mecanismo b´sico de crear o ay mostrar el nuevo tipo:
    • 184 Listas enlazadasclass Nodo: def __init__(self, carga=None, siguiente=None): self.carga = carga self.siguiente = siguiente def __str__(self): return str(self.carga)Como es habitual, los par´metros para el m´todo de inicializaci´n son opciona- a e oles. Por defecto, la carga y el enlace, siguiente, se ponen a None.La representaci´n alfanum´rica de un nodo es unicamente la de la carga. Como o e ´se puede pasar cualquier valor a la funci´n str, podemos guardar cualquier valor oen una lista.Para comprobar la implementaci´n en este punto, podemos crear un Nodo e oimprimirlo:>>> nodo = Nodo("prueba")>>> print nodopruebaPara hacerlo m´s interesante, necesitaremos una lista que contenga m´s de un a anodo:>>> nodo1 = Nodo(1)>>> nodo2 = Nodo(2)>>> nodo3 = Nodo(3)Este c´digo crea tres nodos, pero a´n no tenemos una lista porque los nodos o utodav´ no est´n enlazados. El diagrama de estados tiene el siguiente aspecto: ıa a node1 node2 node3 cargo 1 cargo 2 cargo 3 next None next None next NonePara enlazar los nodos, debemos hacer que el primer nodo haga referencia alsegundo, y que ´ste haga referencia al tercero: e>>> nodo1.siguiente = nodo2>>> nodo2.siguiente = nodo3La referencia del tercer nodo ser´ None, que indica que es el final de la lista. aAhora el diagrama de estados tendr´ el siguiente aspecto: a
    • 17.3 Listas como colecciones 185 node1 node2 node3 cargo 1 cargo 2 cargo 3 next next next NoneAhora ya sabe c´mo crear nodos y enlazarlos en listas. Lo que podr´ estar o ıamenos claro es por qu´. e17.3. Listas como coleccionesLas listas son utiles porque aportan un modo de ensamblar m´ltiples objetos udentro de una unica entidad, a veces llamada colecci´n. En el ejemplo, el primer ´ onodo de la lista sirve como referencia a la lista completa.Para pasar la lista como par´metro, s´lo tenemos que hacer referencia al primer a onodo. Por ejemplo, la funci´n imprimeLista toma como argumento un nodo osimple. Empezando con la cabeza de la lista, imprime cada nodo hasta que llegaal final.def imprimeLista(nodo): while nodo: print nodo, nodo = nodo.siguiente printPara llamar a este m´todo, pasamos una referencia al primer nodo: e>>> imprimeLista(nodo1)1 2 3Dentro de imprimeLista tenemos una referencia al primer nodo de la lista, perono hay una variable que haga referencia a los otros nodos. Tendremos que usarel valor de siguiente de cada nodo para acceder al siguiente nodo.Para recorrer una lista enlazada es habitual usar la variable de un bucle comonodo para hacer referencia sucesivamente a cada uno de los nodos.Este diagrama muestra el valor de lista y los valores que va tomando nodo:
    • 186 Listas enlazadas node1 node2 node3 cargo 1 cargo 2 cargo 3 y y y None node Por convenio, las listas suelen imprimirse entre corchetes con co- mas entre los elementos, como [1, 2, 3]. Como ejercicio, modifique imprimeLista para que genere una salida con este formato.17.4. Listas y recursividadEs natural expresar muchas operaciones con listas por medio de m´todos re- ecursivos. El siguiente ejemplo es un algoritmo recursivo para imprimir una listahacia atr´s: a 1. Separar la lista en dos partes: el primer nodo (llamado cabeza) y el resto (llamado cola). 2. Imprimir la cola hacia atr´s. a 3. Imprimir la cabeza.Por supuesto, el paso 2, la llamada recursiva, supone que tenemos una manerade imprimir una lista del rev´s. Pero si suponemos que la llamada recursiva efunciona —el acto de fe— entonces podemos estar convencidos de que estealgoritmo funciona.Todo lo que necesitamos es un caso b´sico y una forma de demostrar que para acualquier lista podemos obtener el caso b´sico. Dada la definici´n recursiva de a ouna lista, un caso b´sico natural es la lista vac´ representada por None: a ıa,def imprimeAlReves(lista): if lista == None: return cabeza = lista cola = lista.siguiente imprimeAlReves(cola) print cabeza,
    • 17.5 Listas infinitas 187La primera l´ınea maneja el caso inicial no haciendo nada. Las dos siguientesl´ ıneas dividen la lista en cabeza y cola. Las dos ultimas l´ ´ ıneas imprimen lalista. La coma que hay al final de la ultima l´ ´ ınea evita que Python salte de l´ ıneadespu´s de imprimir un nodo. eLlamamos este m´todo tal como antes invocamos a imprimeLista: e>>> imprimeAlReves(nodo1)3 2 1El resultado es una lista invertida.Es posible que se pregunte por qu´ imprimeLista e imprimeAlReves son fun- eciones y no m´todos de la clase Nodo. La raz´n es que queremos usar None para e orepresentar la lista vac´ y no es legal llamar un m´todo sobre None. Esta limi- ıa etaci´n resulta algo inc´moda a la hora de escribir c´digo para manipular listas o o ocon un estilo orientado a objetos puro.¿Podemos demostrar que imprimeAlReves siempre acabar´? En otras palabras, a¿siempre alcanzaremos el caso b´sico? De hecho, la respuesta es no. Algunas alistas har´n que el m´todo no funcione. a e17.5. Listas infinitasNo hay nada que impida a un nodo hacer referencia a un nodo anterior dela lista, incluido ´l mismo. Por ejemplo, esta figura muestra una lista con dos enodos, uno de los cuales apunta a s´ mismo: ı list cargo 1 cargo 2 next nextSi llamamos a imprimeLista sobre esta lista, entrar´ en un bucle infinito. Si lla- amamos a imprimeAlReves, lo har´ de forma infinitamente recursiva. Eeste tipo ade comportamiento da lugar a que sea muy dif´ trabajar con listas infinitas. ıcilSin embargo, en ocasiones resultan utiles. Por ejemplo, podr´ ´ ıamos representarun n´mero como una lista de d´ u ıgitos y usar una lista infinita para representaruna fracci´n repetida. o
    • 188 Listas enlazadasA pesar de todo, es un problema que no podamos probar que imprimeLista eimprimeAlReves puedan terminar correctamente. Lo mejor que podemos haceres afirmar que “si la lista no contiene bucles, estos m´todos podr´n terminar”. e aEste tipo de afirmaciones se conocen como condiciones previas. Imponenuna restricci´n sobre uno de los par´metros y describen el comportamiento del o am´todo si la restricci´n se satisface. Veremos m´s ejemplos m´s adelante. e o a a17.6. Teorema fundamental de la ambig¨ edad uUna parte de imprimeAlReves podr´ habernos sorprendido: ıa cabeza = lista cola = lista.siguienteDespu´s de la primera asignaci´n, la cabeza y la cola tienen el mismo tipo y e oel mismo valor. Asi que, ¿para qu´ hemos creado un nueva variable? eLa raz´n es que las dos variables desempe˜an papeles diferentes. Pensamos en o nla cabeza como una referencia al primer nodo de la lista. Estos “papeles” noforman parte del programa, sino que est´n en la mente del programador. aEn general no podemos decir con s´lo mirar un programa qu´ papel desem- o epe˜ar´ un variable. Esta ambig¨edad puede ser util, pero tambi´n puede difi- n a u ´ ecultar la lectura del programa. A menudo usaremos nombres para las variablescomo nodo y lista para explicar c´mo queremos usar una variable y a veces ocreamos variables adicionales para eliminar ambig¨edades. uPodr´ıamos haber escrito imprimeAlReves sin cabeza ni cola, que lo har´ mas ıaconciso, pero posiblemente menos claro:def imprimeAlReves(lista) : if lista == None : return imprimeAlReves(lista.siguiente) print lista,Mirando esas dos llamadas, hemos de recordar que imprimeAlReves trata susargumentos como una colecci´n y print los trata como a un s´lo objeto. o oEl teorema fundamental de la ambig¨ edad indica que la ambig¨edad que u ues inherente a una referencia a un nodo: Una variable que hace apunta a nodo puede tratar a este nodo como un objeto o como el primero de una lista de nodos.
    • 17.7 Modificar listas 18917.7. Modificar listasHay dos formas de modificar una lista enlazada. Obviamente, podemos cambiarla carga de uno de los nodos, pero las operaciones m´s interesantes son las que aa˜aden, quitan o reordenan los nodos. nComo ejemplo, escribamos un m´todo que quite el segundo nodo de la lista y edevuelva una referencia al nodo quitado:def eliminaSegundo(lista): if lista == None: return primero = lista segundo = lista.siguiente # hacer que el primer noda apunte al tercero primero.siguiente = segundo.siguiente # separar el segundo nodo del resto de la lista segundo.siguiente = None return segundoDe nuevo, estamos usando variables temporales para hacer m´s legible el c´digo. a oAs´ es como usamos este m´todo: ı e>>> imprimeLista(nodo1)1 2 3>>> eliminado = elimnaSegundo(nodo1)>>> imprimeLista(eliminado)2>>> imprimeLista(nodo1)1 3El diagrama de estado nos muestra el efecto de la operaci´n: o first second cargo 1 cargo 2 cargo 3 next next next None¿Qu´ ocurrir´ si llam´ramos a este m´todo y pas´ramos una lista de un unico e ıa a e a ´elemento (un singleton)? ¿Qu´ suceder´ si pas´ramos una lista vac´ como e ıa a ıaargumento? ¿Hay una condici´n previa para este m´todo? Si es as´ ser´ algo o e ı, ıarazonable establecer un m´todo para manejar una violaci´n de la condici´n e o oprevia.
    • 190 Listas enlazadas17.8. Envoltorios y ayudantesA menudo es util dividir una operaci´n de listas en dos m´todos. Por ejemplo, ´ o epara imprimir una lista invertida en el formato convencional [3, 2, 1] pode-mos usar el m´todo imprimeAlReves para imprimir 3, 2, pero necesitaremos eun m´todo aparte para imprimir los corchetes y el primer nodo. Llam´moslo e eimprimeAlRevesBonito:def imprimeAlRevesBonito(lista) : print "[", if lista != None : cabeza = lista cola = lista.siguiente imprimeAlReves(cola) print cabeza, print "]",De nuevo, vemos que es buena idea comprobar m´todos como ´ste para ver si e efuncionan con casos especiales como una lista vac´ o un singleton. ıaCuando usamos este m´todo en alg´n otro lugar del programa, llamamos direc- e utamente a imprimeAlRevesBonito, y ´ste llama a imprimeAlReves en nuestro elugar. En cierto modo, imprimeAlRevesBonito act´a como un envoltorio, y uutiliza a imprimeAlReves como su ayudante.17.9. La clase ListaEnlazadaExisten ciertos problemas delicados con la forma en que se implementaron laslistas. Inviertiendo el orden de causa y efecto, propondremos primero una im-plementaci´n alternativa y explicaremos luego los problemas que ´sta resuelve. o eEn primer lugar crearemos un nueva clase llamada ListaEnlazada. Sus atribu-tos son un entero que contiene la longitud de la lista y una referencia al primernodo. Los objetos ListaEnlazada sirven de asas para la manipulaci´n de las olistas de los objetos de tipo Nodo:class ListaEnlazada : def __init__(self) : self.longitud = 0 self.cabeza = NoneUna ventaja de la clase ListaEnlazada es que suministra un marco naturalpara poner funciones-envoltorio como imprimeAlRevesBonito, que podemosconvertir en un m´todo de la clase ListaEnlazada: e
    • 17.10 Invariantes 191class ListaEnlazada: ... def imprimeAlReves(self): print "[", if self.cabeza != None: self.cabeza.imprimeAlReves() print "]",class Nodo: ... def imprimeAlReves(self): if self.siguiente != None: cola = self.siguiente cola.imprimeAlReves() print self.carga,Para complicar un poco m´s las cosas, renombramos imprimeAlRevesBonito. aAhora hay dos m´todos llamados imprimeAlReves: uno en la clase Nodo (el eayudante), y otro en la clase ListaEnlazada (el envoltorio). Cuando el envolto-rio llama a self.cabeza.imprimeAlReves, est´ llamando al ayudante, ya que aself.cabeza es un objeto de tipo Nodo.Otra ventaja de la clase ListaEnlazada es que facilita la forma de a˜adir o qui- ntar el primer elemento de una lista. Por ejemplo, agregaPrimero es un m´todo epara ListaEnlazada; toma un elemento de la carga como argumento y lo colocaen el principio de la lista:class ListaEnlazada: ... def agregaPrimero(self, carga): nodo = Nodo(carga) nodo.siguiente = self.cabeza self.cabeza = nodo self.longitud = self.longitud + 1Como suele ser usual, deber´ ıamos comprobar ´ste c´digo para ver si maneja e ocasos especiales. Por ejemplo, ¿qu´ ocurrir´ si la lista est´ unicialmente vac´ e ıa a ıa?17.10. InvariantesAlgunas listas est´n “bien construidas”; otras no. Por ejemplo, si una lista con- atiene un bucle, provocar´ que nuestros m´todos se cuelguen, as´ que podr´ a e ı ıamosexigir que las listas no contengan bucles. Otro requisito es que el valor de el valor
    • 192 Listas enlazadasde longitud en el objeto de tipo ListaEnlazada deber´ ser igual al n´mero ıa uverdadero de nodos de la lista.A este tipo de requisitos los llamaremos invariantes porque, idealmente de-ber´ cumplirse en todos los objetos en todo momento. Especificar invariantes ıanpara objetos es una pr´ctica util de la programaci´n porque hace m´s f´cil de- a ´ o a amostrar la idoneidad del c´digo, comprobar la integridad de las estructuras de odatos y la detecci´n de errores. oUna cosa que a veces confunde respecto a los invariantes es que en ocasiones sonviolados. Por ejemplo, en medio de agregaPrimero, despu´s de a˜adir el nodo e nparo antes de incrementar la longitud, se viola el invariante. Se acepta este tipode violaci´n; de hecho, a menudo es imposible modificar un objeto sin violar un oinvariante durante al menos un corto espacio de tiempo. Normalmente, se exigeque todo m´todo que viole un invariante debe restablecerlo. eSi hay alg´n tramo significativo de c´digo en el que el invariante se ve violado, u oes importante que los comentarios lo dejen claro, de modo que no se realicenoperaciones que dependan del invariante.17.11. Glosarioreferencia incrustada: Es una referencia almacenada en un atributo de un objeto.lista enlazada: Estructura de datos que implementa una colecci´n por medio o de una secuencia de nodos enlazados.nodo: Elemento de una lista, normalmente implementado como un objeto que contiene una referencia a otro objeto del mismo tipo.carga: Datos contenidos en un nodo.enlace: Referencia incrustada usada para enlazar un objeto con otro.condici´n previa: Afirmaci´n que debe ser cierta para que un m´todo funcio- o o e ne correctamente.teorema fundamental de la ambig¨ edad: Una referencia a un nodo de una u lista puede tratarse como un objeto individual o como el primero de una lista de nodos.singleton: Lista enlazada con un solo nodo.
    • 17.11 Glosario 193envoltorio: M´todo que act´a como mediador entre un m´todo invocador y e u e m´todo ayudante, haciendo a menudo su invocaci´n m´s f´cil o menos e o a a proclive a errores.ayudante: M´todo al que no se invoca directamente por un m´todo llamante e e sino por otro m´todo para formar parte de una operaci´n. e oinvariante: Afirmaci´n que deber´ ser cierta para un objeto en todo momento o ıa (excepto tal vez cuando se est´ modificando el objeto). a
    • Cap´ ıtulo 18Pilas18.1. Tipos abstractos de datosLos tipos de datos vistos hasta ahora han sido todos concretos, en el sentidoque se ha especificado su implementaci´n completamente. Por ejemplo, la clase oCarta representa un naipe por medio de dos enteros. Como dijimos, esa no esla unica manera de representar una carta; existen muchas implementaciones ´alternativas.Un tipo abstracto de datos, o TAD, especifica un conjunto de operaciones(o m´todos) y la sem´ntica de las operaciones (lo que hacen), pero no especifica e ala implementaci´n de las operaciones. Esto es lo hace lo abstracto. o¿Para qu´ sirve? e Simplifica la tarea de especificar un algoritmo si se pueden indicar las operaciones necesarias sin preocuparse de c´mo se ejecutar´n dichas ope- o a raciones. Como suelen existir muchas maneras de implementar un TAD, puede ser util desarrollar un algoritmo que se pueda usar con cualquiera de las im- ´ plementaciones posibles. Los TADs mas conocidos, como el TAD Pila de este cap´ ıtulo, se implemen- tan a menudo en bibliotecas est´ndares de manera que se puedan escribir a una vez y usarlas luego muchos programadores. Las operaciones ejecutadas con TADs proporcionan un lenguaje de alto nivel com´n para desarrollar y hablar sobre algoritmos. u
    • 196 PilasCuando hablamos de TADs a menudo se hace la distinci´n entre el c´digo que o ousa el TAD, el c´digo cliente, y el c´digo que implementa el TAD, el c´digo o o oproveedor.18.2. El TAD PilaEn este cap´ıtulo se presentar´ un TAD com´n, la pila. Una pila es una colecci´n, a u olo que significa que es una estructura de datos que contiene elementos m´ltiples. uOtras colecciones que se han visto son los diccionarios y las listas.Un TAD se definido por medio de las operaciones que se pueden ejecutar so-bre ´l, lo que se llama un interfaz. La interfaz para una pila consta de estas eoperaciones1 : init : Inicializar una pila nueva y vac´ ıa.push: A˜adir un elemento a la pila. npop: Extraer un elemento de la pila. El elemento devuelto siempre es el ultimo ´ que se a˜adi´. n oisEmpty: Probar si la pila est´ vac´ a ıa.A veces a una pila se la llama una estructura “´ltimo en entrar primero en salir” u(“last in, first out” en ingl´s), o LIFO, porque el elemento a˜adido en ultimo e n ´lugar es el primero que extraemos.18.3. C´mo implementar pilas con listas de Pyt- o honLas operaciones sobre listas que posee Python son parecidas a las operacionesque definen a una pila. La interfaz no es exactamente la que deber´ de ser, pero ıase pueden desarrollar programas para convertir el TAD Pila a las operacionespredefinidas.A este programa se lo llama implementaci´n del TAD Pila. En general, una oimplementaci´n es un conjunto de m´todos que satisfacen los prerrequisitos o esint´cticos y sem´nticos de la interfaz. a aHe aqu´ una implementaci´n de el TAD Pila que utiliza una lista Python: ı o 1 Mantenemos los nombres ingleses porque son est´ndar en otros TADs en Python y otros alenguajes.
    • 18.4 Uso de push y pop 197class Pila : def __init__(self) : self.elementos = [] def push(self, elemento) : self.elementos.append(elemento) def pop(self) : return self.elementos.pop() def isEmpty(self) : return (self.elementos == [])Un objeto Pila contiene un atributo llamado elementos que es una lista de ele-mentos en la pila. El m´todo de inicializaci´n pone una lista vac´ en elementos. e o ıaPara meter un elemento nuevo en la pila, push lo apila en elementos. Paraquitar un elemento de la pila, pop utiliza el m´todo de lista hom´nimo2 para e oquitar y devolver el ultimo elemento de la lista. ´Finalmente, para probar si la pila esta vac´ isEmpty (est´ vac´ compara ıa, a ıa)elementos con la lista vac´ ıa.Una implementaci´n como esta, en la cual los m´todos consisten de llamadas a o em´todos existentes, se llama enchapado. En la vida real, un enchapado es una ecapa fina de madera de alta calidad que se utiliza en la fabricaci´n de muebles opara ocultar madera de baja calidad. Los cient´ıficos inform´ticos utilizan esta amet´fora para describir una parte de un programa que esconde los detalles de auna implementaci´n y que provee una interfaz mas simple o mas est´ndar. o a18.4. Uso de push y popUna pila es una estructura gen´rica de datos, lo significa que se puede a˜adir e ncualquier tipo de elementos a ella. El ejemplo mete en la pila dos enteros y unacadena:>>> s = Stack()>>> s.push(54)>>> s.push(45)>>> s.push("+")Se pueden utilizar isEmpty y pop para quitar e imprimir todos los elementos enla pila: 2 del mismo nombre
    • 198 Pilaswhile not s.isEmpty() : print s.pop(),La salida es + 45 54. En otras palabras, ¡se ha utilizado una pila para imprimirlos elementos en orden inverso! Reconocemos que no es el formato est´ndar de aimpresi´n de listas, pero fue muy f´cil usar una pila para lograrlo. o aCompare estas l´ ıneas con la implementaci´n de imprimeAlReves que vimos oen la Secci´n 17.4. Existe un paralelo natural entre la versi´n recurrente de o oimprimeAlReves y el algoritmo de pila que acabamos de ver. La diferencia esque imprimeAlReves utiliza la pila de tiempo de ejecuci´n para mantenerse al otanto de los nodos mientras recorre la lista y luego los imprime cuando regresade la recursi´n. El algoritmo de pila hace lo mismo, pero utiliza un objeto Pila oen vez de la pila de tiempo de ejecuci´n. o18.5. Usar una pila para evaluar postfijoLas expresiones matem´ticas en la mayor´ de lenguajes de programaci´n se a ıa oescriben con el operador entre los dos operandos, de esta manera: 1+2. A esteformato se le llama infijo. Algunas calculadoras utilizan un formato alternativollamado postfijo. Con postfijo, el operador va despu´s de los operandos, as´ 1 e ı:2 +.La raz´n por la que el formato postfijo es util es que existe una forma natural o ´de evaluar una expresi´n en formato postfijo utilizando una pila: o Desde el principio de la expresi´n, eval´e los operadores y operandos uno o u por uno. • Si el t´rmino es un operando, utilice push para colocarlo en la pila. e • Si el t´rmino es un operador, utilice pop con dos operandos de la pila, e ejecute la operaci´n sobre ellos, y coloque el resultado en la pila con o push. Cuando llegue al final de la expresi´n habr´ un operando en la pila. Ese o a operando es el resultado. Para practicar, aplique este algoritmo a la expresi´n 1 2 + 3 *. oEste ejemplo demuestra una de las ventajas de el formato postfijo—no haynecesidad de usar par´ntesis para controlar el orden de operaciones. Para obtener eel mismo resultado con el formato infijo, se tendr´ que escribir (1 + 2) * 3). ıa Para practicar, escriba una expresi´n en formato postfijo que sea o equivalente a 1 + 2 * 3.
    • 18.6 An´lisis sint´ctico a a 19918.6. An´lisis sint´ctico a aPara implementar el algoritmo anterior, necesitamos ser capaces de recorrer unacadena y separarla en operandos y operadores. Este proceso es un ejemplo delan´lisis sint´ctico, y los resultados—la piezas individuales de la cadena—son a atokens3 . Quiz´s se acuerde de esas palabras del Cap´ a ıtulo 1.Python posee un m´todo llamado split (partir) en el m´dulo string (cadena) e oy en el m´dulo re (expresiones regulares). La funci´n string.split parte una o ocadena y la convierte en una lista utilizando un s´lo car´cter como delimitador. o aPor ejemplo:>>> import string>>> string.split("La hora ha llegado"," ")[’La’, ’hora’, ’ha’, ’llegado’]En este caso, el delimitador es el car´cter espacio, y se parte la cadena en cada aespacio.La funci´n re.split tiene m´s potente, y nos permite utilizar una expresi´n o a oregular en vez de un delimitador. Una expresi´n regular es una manera de espe- ocificar un conjunto de cadenas. Por ejemplo, [A-z] es el conjunto de todas lasletras y [0-9] es el conjunto de todas las cifras. El operador ^ niega un conjunto,y [^0-9] es el conjunto de todo lo que no es un n´mero, lo cual es exactamente ulo que queremos utilizar para partir expresiones en el formato postfijo:>>> import re>>> re.split("([^0-9])", "123+456*/")[’123’, ’+’, ’456’, ’*’, ’’, ’/’, ’’]F´ ıjese que el orden de los argumentos es diferente del de string.split; eldelimitador viene antes de la cadena.La lista resultante incluye los operandos 123 y 456 y los operadores * y /.Tambi´n incluye dos cadenas vac´ que se insertan despu´s de los operandos. e ıas e18.7. Evaluar un postfijoPara evaluar una expresi´n en formato postfijo usaremos el analizador sint´ctico o ade la secci´n anterior y el algoritmo de la secci´n previa a ´sa. Para no complicar o o e 3 Podr´ıamos traducir “token” como “cospel” o “pieza”, en ocasiones tambi´n como “s´ e ımbo-lo” pero la expresi´n inglesa est´ tan introducida en el vocabulario inform´tico que s´lo o a a oa˜adir´ confusi´n. n ıa o
    • 200 Pilaslas cosas, comenzaremos con un evaluador que solo implementa los operadores+ y *:def evalPostfijo(expr): import re listaTokens = re.split("([^0-9])", expr) pila = Pila() for token in listaTokens: if token == ’’ or token == ’ ’: continue if token == ’+’: suma = pila.pop() + pila.pop() pila.push(suma) elif token == ’*’: producto = pila.pop() * pila.pop() pila.push(producto) else: pila.push(int(token)) return pila.pop()La primera condici´n se encarga de espacios y cadenas vac´ Las dos condi- o ıas.ciones siguientes controlan los operadores. Asumimos, por ahora, que todo lodem´s es un operando. Por supuesto, ser´ mejor verificar si la entrada tiene a ıaerrores y mostrar un mensaje con el error, pero eso se har´ despu´s. a eComprobemos con una evaluaci´n de la forma postfijo (56+47)*2): o>>> print evalPostfijo ("56 47 + 2 *")206Esto es suficiente.18.8. Clientes y proveedoresUno de los objetivos fundamentales de un TAD es el de separar los interesesdel proveedor, quien escribe el c´digo de programa que implementa el TAD, oy los del cliente, quien utiliza el TAD. El proveedor s´lo se preocupa de la oimplementaci´n y de si es correcta o no—de acuerdo a las especificaciones del oTAD—y no de c´mo se va a utilizar. o
    • 18.9 Glosario 201A la inversa, el cliente supone que la implementaci´n del TAD es correcta y ono se preocupa de los detalles. Cuando se usa uno de los tipos predefinidos dePython, uno se puede dar el lujo de pensar exclusivamente como un cliente.Por supuesto, cuando usted implemente un TAD, tambi´n debe desarrollar c´di- e ogo de cliente para probarlo. En ese case, uno toma ambos papeles, lo cual puedecausar confusi´n. Tiene que fijarse bien en del papel que est´ tomando en todo o amomento.18.9. Glosariotipo abstracto de datos (TAD): Un tipo de datos (a menudo una colecci´n o de objetos) que se define por un conjunto de operaciones pero que se puede implementar de varias maneras.interfaz: El conjunto de operaciones que definen un TAD.implementaci´n: El c´digo de programa que satisface los prerrequisitos o o sint´cticos y sem´nticos de un interfaz. a acliente: Un programa (o la persona que lo escribi´) que utiliza un TAD. oproveedor: Un programa (o la persona que lo escribi´) que implementa un o TAD.enchapado: La definici´n de clase que implementa un TAD con definiciones o de m´todos que son las invocaciones de otros m´todos, a veces con trans- e e formaciones simples. El enchapado no ejecuta nada de gran valor, pero mejora la interfaz vista por el cliente o la hace mas est´ndar. aestructura de datos gen´rica: Un tipo de estructura de datos que puede e contener datos de cualquier tipo.infijo: Un m´todo de escribir expresiones matem´ticas con los operadores entre e a los operandos.postfijo: Un m´todo de escribir expresiones matem´ticas con los operadores e a despu´s de los operandos. eanalizar sint´cticamente: Examinar una cadena de caracteres o tokens y a analizar su estructura gram´tical. atoken: Un conjunto de caracteres que se tratan como una unidad y son anali- zados sint´cticamente, como las palabras de un lenguaje natural. adelimitador: Un car´cter utilizado para separar tokens, como la puntuaci´n a o en un lenguaje natural.
    • Cap´ ıtulo 19ColasEste cap´ ıtulo presenta dos TADs: la Cola y la Cola Priorizada. En la vida real,una cola es una fila de clientes esperando un servicio de alg´n tipo. En la umayor´ de los casos, el primer cliente de la fila es el primero al que se va a ıaservir. Sin embargo, hay excepciones. En los aeropuertos, a veces se saca de lacola a los clientes cuyos vuelos van a salir pronto. En los supermercados, uncliente educado puede dejar que alguien que lleva pocos productos pase antes.La regla que determina qui´n va primero se llama t´ctica de encolamiento. e aLa t´ctica de encolamiento m´s simple se llama FIFO, de “first-in-first-out”, a a“el primero que entra es el primero que sale”. La t´ctica de encolamiento m´s a ageneral es el encolamiento priorizado, en la que a cada cliente se le asignauna prioridad y el cliente con la prioridad m´s alta pasa primero, sin importar ael orden de llegada. Decimos que es la t´ctica m´s general porque la prioridad a ase puede basar en cualquier cosa: a qu´ hora sale el vuelo; cu´ntos productos e alleva el cliente; cu´n importante es el cliente. Por supuesto, no todas las t´cticas a ade prioridad son “justas”, pero la justicia siempre es subjetiva.El TAD Cola y el TAD Cola Priorizada tienen el mismo conjunto de operaciones.La diferencia est´ en la sem´ntica de las operaciones: una cola usa la t´ctica a a aFIFO, y una cola priorizada (como su propio nombre indica) usa una t´ctica de aencolamiento priorizado.19.1. El TAD ColaEl TAD Cola se define a trav´s de las siguientes operaciones: e init : Inicializa una cola nueva vac´ ıa.
    • 204 Colasinserta: A˜ade un elemento a la cola. nquita: Elimina y devuelve un elemento de la cola. El elemento devuelto es el primero que se a˜adi´. n oestaVacia: Comprueba si la cola est´ vac´ a ıa.19.2. Cola EnlazadaLa primera implementaci´n del TAD Cola al que vamos a echar un vistazo se ollama cola enlazada porque est´ hecha de ojetos Nodo enlazados. He aqu´ la a ıdefinici´n de la clase: oclass Cola: def __init__(self): self.longitud = 0 self.cabeza = None def estaVacia(self): return (self.longitud == 0) def inserta(self, carga): nodo = Nodo(carga) nodo.siguiente = None if self.cabeza == None: # si la lista est´ vac´a el nuevo nodo va el primero a ı self.cabeza = nodo else: # encuentra el ´ltimo nodo de la lista u ultimo = self.cabeza while ultimo.siguiente: ultimo = ultimo.siguiente # a~adir el nuevo nodo n ultimo.siguiente = nodo self.longitud = self.longitud + 1 def quita(self): carga = self.cabeza.carga self.cabeza = self.cabeza.siguiente self.longitud = self.longitud - 1 return cargaLos m´todos estaVacia y quita son id´nticos a los m´todos estaVacia y e e equitaPrimero de ListaEnlazada. El m´todo inserta es nuevo y un poco m´s e a
    • 19.3 Rendimiento t´ ıpico 205complicado.Queremos insertar nuevos elementos al final de la lista. Si la cola est´ vac´ a ıa,simplemente hacemos que cabeza se refiera al nuevo nodo.En caso contrario, recorremos la lista hasta el ultimo nodo y lo fijamos al final. ´Podemos reconocer el ultimo nodo porque su atributo siguiente es None. ´En un objeto Cola correctamente construido hay dos invariantes. El valor delongitud deber´ ser el n´mero de nodos en la cola, y el ultimo nodo deber´ ıa u ´ ıatener siguiente igual a None. Cr´ase que este m´todo cumple con ambas inva- e eriantes.19.3. Rendimiento t´ ıpicoNormalmente cuando invocamos un m´todo, no nos importan los detalles de su eimplementaci´n. Pero hay un “detalle” que podr´ interesarnos: el rendimien- o ıato t´ ıpico del m´todo. ¿Cu´nto tarda, y c´mo var´ el tiempo de ejecuci´n al e a o ıa oaumentar el n´mero de elementos de la colecci´n? u oPrimero mire quita. Ah´ no hay bucles ni llamadas a funciones, dando a enten- ıder que el tiempo de ejecuci´n de este m´todo es siempre el mismo. Un m´todo o e eas´ se llama operaci´n de tiempo constante. En realidad, el m´todo podr´ ı o e ıaser ligeramente m´s r´pido cuando la lista est´ vac´ porque se salta el cuerpo a a a ıade la condici´n, pero esa diferencia no es significativa. oEl rendimiento de inserta es muy diferente. En el caso general, tenemos querecorrer la lista para encontrar el ultimo elemento. ´Este recorrido cuesta un tiempo proporcional a la longitud de la lista. Como eltiempo de ejecuci´n es funci´n lineal de la longitud, este m´todo se llama de o o etiempo lineal. Comparado con el tiempo constante, es muy pobre.19.4. Cola Enlazada MejoradaNos gustar´ una implementaci´n del TAD Cola capaz de realizar todas las ıa ooperaciones en tiempo constante. Una forma de hacerlo es modificar la claseCola de modo que mantenga una referencia tanto al primero como al ultimo ´nodo, como se muestra en la figura:
    • 206 Colas head length 3 last cargo 1 cargo 2 cargo 3 next next nextLa implementaci´n de ColaMejorada es as´ o ı:class ColaMejorada: def __init__(self): self.longitud = 0 self.cabeza = None self.ultimo = None def estaVacia(self): return (self.longitud == 0)Hasta ahora, el unico cambio es el atributo ultimo. Se usa en los m´todos ´ einserta y quita:class ColaMejorada: ... def inserta(self, carga): nodo = Nodo(carga) nodo.siguiente = None if self.longitud == 0: # si la lista est´ vac´a, el nuevo nodo es cabeza y ´ltimo a ı u self.cabeza = self.ultimo = nodo else: # encontrar el ´ltimo nodo u ultimo = self.ultimo # a~adir el nuevo nodo n ultimo.siguiente = nodo self.ultimo = nodo self.longitud = self.longitud + 1Como ultimo sigue el rastro del ultimo nodo, no necesitamos buscarlo. A causa ´de esto, este m´todo funciona en tiempo constante. eDebemos pagar un precio por esa velocidad. Tenemos que a˜adir un caso especial na quita para apuntar ultimo a None cuando quitamos el ultimo nodo: ´class ColaMejorada:
    • 19.5 Cola priorizada 207 ... def quita(self): carga = self.cabeza.carga self.cabeza = self.cabeza.siguiente self.longitud = self.longitud - 1 if self.longitud == 0: self.ultimo = None return cargaEsta implementaci´n es m´s complicada que la de la Lista Enlazada, y es m´s o a adif´ demostrar que es correcta. La ventaja es que hemos alcanzado la meta: ıciltanto inserta como quita son operaciones de tiempo constante. Como ejercicio, escriba una implementaci´n del TAD Cola usando o una lista de Python. Compare el rendimiento de esta implementaci´n o con la de la ColaMejorada para varias longitudes de cola.19.5. Cola priorizadaEl TAD Cola Priorizada tiene el mismo interfaz que el TAD Cola, pero diferentesem´ntica. De nuevo, el interfaz es: a init : Inicializa una cola vac´ nueva. ıainserta: A˜ade un nuevo elemento a la cola. nquita: Elimina y devuelve un elemento de la cola. El elemento devuelto es el de prioridad m´s alta. aestaVacia: Comprueba si la cola est´ vac´ a ıa.La diferencia sem´ntica es que el elemento eliminado de la cola no es necesa- ariamente el primero que se a˜adi´. En su lugar, es el elemento con la prioridad n om´s alta. Lo que son las prioridades y c´mo se comparan con las otras no se a oespecifica en la implementaci´n de la Cola Priorizada. Depende de los elementos ode la cola.Por ejemplo, si los elementos de la cola tienen nombres, podemos elegirlos enorden alfab´tico. Si son puntuaciones de bolos, podemos ir de mayor a menor, epero si son puntuaciones de golf, iremos de menor a mayor. Siempre que podamoscomparar los elementos de la cola, podremos encontrar y quitar el elemento conmayor prioridad.Esta implementaci´n de Cola Priorizada tiene como atributo una lista de Python oque contiene los elementos de la cola.
    • 208 Colasclass ColaPriorizada: def __init__(self): self.elementos = [] def estaVacia(self): return self.elementos == [] def inserta(self, elemento): self.elementos.append(elemento)El m´todo de inicializaci´n, estaVacia, e inserta son todos calcados de las e ooperaciones sobre listas. El unico m´todo interesante es quita: ´ eclass ColaPriorizada: ... def quita(self): maxi = 0 for i in range(1,len(self.elementos)): if self.elementos[i] > self.elementos[maxi]: maxi = i elemento = self.elementos[maxi] self.elementos[maxi:maxi+1] = [] return elementoAl principio de cada iteraci´n, maxi contiene el ´ o ındice del elemento m´s grande a(prioridad m´s alta) que hemos visto hasta el momento. Cada vez que se com- apleta el bucle, el programa compara el i´simo elemento con el campe´n. Si el e onuevo elemento es mayor, el valor de maxi se fija a i.Cuando la sentencia for completa su ejecuci´n, maxi es el ´ o ındice del elementomayor. Este elemento se elimina de la lista y se devuelve.Vamos a probar la implementaci´n: o>>> c = ColaPriorizada()>>> c.inserta(11)>>> c.inserta(12)>>> c.inserta(14)>>> c.inserta(13)>>> while not c.estaVacia(): print c.quita() # ver cu´l se quita a14131211
    • 19.6 La clase Golfista 209Si la cola contiene n´meros o cadenas simples, se eliminan en orden num´rico u eo alfab´tico, de mayor a menor. Python puede encontrar el entero o la cadena emayor porque puede compararlos usando los operadores de comparaci´n inter- onos.Si la cola contiene un tipo de objeto, debe proporcionar un m´todo cmp . eCuando quita usa el operador > para comparar elementos, invoca al cmp deuno de los elementos y le pasa el otro como par´metro. Siempre que el m´todo a e cmp trabaje adecuadamete, la Cola Priorizada funcionar´. a19.6. La clase GolfistaComo ejemplo de un objeto con una definici´n inusual de prioridad, vamos a oimplementar una clase llamada Golfista que mantiene los nombres y puntua-ciones de golfistas. Como es habitual, empezamos por definir init y str :class Golfista: def __init__(self, nombre, puntos): self.nombre = nombre self.puntos = puntos def __str__(self): return "%-16s: %d" % (self.nombre, self.puntos) str usa el operador de formato para poner los nombres y las puntuacionesen bonitas columnas.A continuaci´n definimos una versi´n de cmp en la que la puntuaci´n m´s o o o abaja tiene la prioridad m´s alta. Como siempre, cmp devuelve 1 si self es a“mayor que” otro, -1 si self es “menor que” otro, y 0 si son iguales.class Golfista: ... def __cmp__(self, otro): if self.puntos < otro.puntos: return 1 # menos es m´s a if self.puntos > otro.puntos: return -1 return 0Ya estamos listos para probar la cola priorizada con la clase Golfista:>>> tiger = Golfista("Tiger Woods", 61)>>> cabr = Golfista("´ngel Cabrera", A 72)>>> ola = Golfista("J.M. Olaz´bal", a 69)>>>
    • 210 Colas>>> cp = ColaPriorizada()>>> cp.inserta(tiger)>>> cp.inserta(cabr)>>> cp.inserta(ola)>>> while not cp.estaVacia(): print cp.quita()Tiger Woods : 61J.M. Olaz´bal : 69 a´ngel Cabrera : 72A Como ejercicio, escriba una implementaci´n del TAD Cola Prio- o rizada usando una lista enlazada. Deber´ usted mantener la lista ıa ordenada de modo que la eliminaci´n sea una operaci´n de tiempo o o constante. Compare el rendimiento de esta implementaci´n con la o implementaci´n con la lista de Python. o19.7. Glosariocola: Un conjunto ordenado de objetos esperando un servicio de alg´n tipo. uCola: Un TAD que ejecuta las operaciones que uno podr´ realizar sobre una ıa cola.t´ctica de encolamiento: Las reglas que determinan qu´ miembro de la cola a e ser´ el pr´ximo en eliminarse. a oFIFO: “First In, First Out”, una t´ctica de encolamiento en la que el primer a miembro en llegar es el primero en salir.cola priorizada: Una t´ctica de encolamiento en la que cada miembro tiene a una prioridad determinada por factores externos. El miembro con mayor prioridad es el primero en eliminarse.Cola Priorizada: Un TAD que define las operaciones que se pueden realizar sobre una cola priorizada.cola enlazada: Una implementacion de una cola utilizando una lista enlazada.tiempo constante: Una operaci´n cuyo tiempo de ejecuci´n no depende del o o tama˜o de la estructura de datos. ntiempo lineal: Una operaci´n cuyo tiempo de ejecuci´n es funci´n lineal del o o o tama˜o de la estructrua de datos. n
    • Cap´ ıtulo 20´ArbolesAl igual que las listas enlazadas, los ´rboles est´n hechos de nodos. Un tipo a acom´n de ´rbol es un ´rbol binario, en el que cada nodo contiene una referencia u a aa otros dos nodos (posiblemente nula). Nombramos a estas referencias comosub´rboles izquierdo y derecho. Como los nodos de las listas, los nodos de los a´rboles tambi´n contienen una carga. Un diagrama de estado de un ´rbol es as´a e a ı: tree cargo 1 left right cargo 2 cargo 3 left right left right None None None NonePara evitar apelotonar las cosas en la figura, solemos omitir los Nones.La parte superior del ´rbol (el nodo al que apunta tree) se llama ra´ Siguiendo a ız.con la met´fora del ´rbol, los otros nodos se llaman ramas y los nodos de los a aextremos con referencias nulas se llaman hojas. Puede parecer extra˜o quendibujemos la figura con la ra´ arriba y las hojas abajo, pero eso no es lo m´s ız araro.Para empeorar las cosas, los cient´ıficos inform´ticos a˜aden a la mezcla otra a nmet´fofa: el ´rbol geneal´gico. El nodo superior se llama a veces padre y los a a o
    • 212 ´ Arbolesnodos a los que se refiere son sus hijos. Los nodos con el mismo padre se llamanhermanos.Para terminar, tenemos un vocabulario geom´trico para hablar sobre los ´rboles. e aYa hemos mencionado izquierda y derecha, pero tambi´n est´n “arriba” (hacia e ael padre/ra´ y “abajo” (hacia los hijos/hojas). Tambi´n, todos los nodos que ız) eest´n a la misma distancia de la ra´ forman un nivel del ´rbol. a ız aProbablemente no sean necesarias las met´foras arb´reas para hablar de ´rboles, a o apero ah´ est´n. ı aIgual que las listas enlazadas, los ´rboles son estructuras de datos recursivas aporque se definen recursivamente. Un ´rbol es: a el ´rbol vac´ representado por None, o a ıo, un nodo que contiene una referencia a un objeto y dos referen- cias a ´rboles. a20.1. Crear ´rboles aEl proceso de montar un ´rbol es similar al proceso de montar una lista enlazada. aCada invocaci´n del constructor crea un solo nodo. oclass Arbol: def __init__(self, carga, izquierda=None, derecha=None): self.carga = carga self.izquierda = izquierda self.derecha = derecha def __str__(self): return str(self.carga)La carga puede ser de cualquier tipo, pero los par´metros izquierda y derecha adeben ser ´rboles. Tanto izquierda como derecha son opcionales; el valor por aomisi´n es None. oPara imprimir un nodo, simplemente imprimimos la carga.Una forma de construir un ´rbol es del fondo hacia arriba. Asigne primero los anodos hijos:izquierda = Arbol(2)derecha = Arbol(3)
    • 20.2 Recorrer ´rboles a 213Luego cree el nodo padre y vinc´lelo a los hijos: uarbol = Arbol(1, izquierda, derecha);Podemos escribir este c´digo m´s concisamente anidando las invocaciones al o aconstructor:>>> arbol = Arbol(1, Arbol(2), Arbol(3))En cualquier caso, el resultado es el ´rbol del principio del cap´ a ıtulo.20.2. Recorrer ´rboles aSiempre que usted vea una nueva estructura de datos, su primera preguntadeber´ ser: “¿C´mo la recorro?” La forma m´s natural de recorrer un ´rbol ıa o a aes recursivamente. Por ejemplo, si el ´rbol contiene enteros como carga, esta afunci´n nos devuelve su suma: odef total(arbol): if arbol == None: return 0 return total(arbol.izquierda) + total(arbol.derecha) + arbol.cargaEl caso base es el ´rbol vac´ que no tiene carga, as´ que la suma es 0. El a ıo, ıpaso recursivo hace dos llamadas recursivas para hallar la suma de los ´rboles ahijos. Cuando terminan las llamadas recursivas, sumamos la carga del padre ydevolvemos el total.20.3. ´ Arboles de expresi´n oUn ´rbol es un forma natural de representar la estructura de una expresi´n. a oAl contrario que otras notaciones, puede representar el c´lculo de forma no aambigua. Por ejemplo, la expresi´n infija 1 + 2 * 3 es ambigua a no ser que osepamos que la multiplicaci´n se realiza antes que la suma. o´Este ´rbol de expresi´n representa el mismo c´lculo: a o a
    • 214 ´ Arboles tree cargo + left right cargo 1 cargo left right left * right cargo 2 cargo 3 left right left rightLos nodos de un ´rbol de expresi´n pueden ser operandos como 1 y 2 u operado- a ores como + y *. Los operandos son nodos hojas; los nodos operadores contienenreferencias a sus operandos. (Todos estos operadores son binarios, lo que sig-nifica que tienen exactamente dos operandos.)As´ podemos construir este ´rbol: ı a>>> arbol = Arbol(’+’, Arbol(1), Arbol(’*’, Arbol(2), Arbol(3)))Mirando la figura, no hay duda del orden de las operaciones; la multiplicaci´n ose realiza antes para calcular el segundo operando de la suma.Los ´rboles de expresi´n tienen muchos usos. El ejemplo de este cap´ a o ıtulo usa ´´rboles para traducir expresiones a postfijo, prefijo e infijo. Arboles similares seausan dentro de los compiladores para analizar, optimizar y traducir programas.20.4. Recorrido de un ´rbol aPodemos recorrer un ´rbol de expresi´n e imprimir el contenido as´ a o ı:def imprimeArbol(arbol): if arbol == None: return print arbol.carga, imprimeArbol(arbol.izquierda) imprimeArbol(arbol.derecha)En otras palabras, para imprimir un ´rbol imprima primero el contenido de la ara´ luego el sub´rbol izquierdo entero y despu´s el sub´rbol derecho entero. ız, a e aEsta forma de recorrer un ´rbol se llama orden prefijo, porque el contenido de ala ra´ aparece antes del contenido de los hijos. La salida del ejemplo anterior ızes:
    • 20.4 Recorrido de un ´rbol a 215>>> arbol = Arbol(’+’, Arbol(1), Arbol(’*’, Arbol(2), Arbol(3)))>>> imprimeArbol(arbol)+ 1 * 2 3Este formato es diferente tanto del postfijo como del infijo; es otra notaci´n ollamada prefija, en la que los operadores aparecen delante de sus operandos.Puede sospechar que si recorre el ´rbol en un orden diferente obtendr´ la expre- a asi´n en una notaci´n diferente. Por ejemplo, si imprime primero los sub´rboles o o ay luego la ra´ tendr´: ız, adef imprimeArbolPostfijo(arbol): if arbol == None: return imprimeArbolPostfijo(arbol.izquierda) imprimeArbolPostfijo(arbol.derecha) print arbol.carga,¡El resultado, 1 2 3 * +, est´ en notaci´n posfija! Este orden de recorrido se a ollama orden postfijo.Finalmente, para recorrer un ´rbol en orden infijo, usted imprime el ´rbol a aizquierdo, luego la ra´ y despu´s el ´rbol derecho: ız e adef imprimeArbolInfijo(arbol): if arbol == None: return imprimeArbolInfijo(arbol.izquierda) print arbol.carga, imprimeArbolInfijo(arbol.derecha)El resultado es 1 + 2 * 3, que es la expresi´n en notaci´n infija. o oPara ser justos debemos se˜alar que hemos omitido una importante compli- ncaci´n. A veces, al escribir una expresi´n infija, debemos usar par´ntesis para o o epreservar el orden de las operacioens. De modo que una exploraci´n de orden oinfijo no es suficiente para generar una expresi´n infija. oNo obstante, con unas peque˜as mejoras, el ´rbol de expresi´n y los tres re- n a ocorridos nos dan una forma general de traducr expresiones de un formato aotro. Como ejercicio, modifique imprimeArbolInfijo de modo que pon- ga entre par´ntesis cada operador con sus operandos. ¿La salida es e correcta y sin ambig¨edades? ¿Se necesitan siempre los par´ntesis? u eSi hacemos un recorrido en orden infijo y tomamos nota de en qu´ nivel del e´rbol estamos, podemos generar una representaci´n gr´fica de un ´rbol:a o a a
    • 216 ´ Arbolesdef imprimeArbolSangrado(arbol, nivel=0): if arbol == None: return imprimeArbolSangrado(arbol.derecha, nivel+1) print ’ ’*nivel + str(arbol.carga) imprimeArbolSangrado(arbol.izquierda, nivel+1)El par´metro nivel lleva la cuenta de d´nde estamos en el ´rbol. Por omisi´n, a o a oincialmente es 0. Cada vez que hacemos una llamada recursiva, pasamos nivel+1porque el nivel del hijo es siempre uno mayor que el del padre. Sangramos cadaelemento con dos espacios por nivel. El resultado del ´rbol de ejemplo es: a>>> imprimeArbolSangrado(arbol) 3 * 2+ 1Si mira la salida de lado, ver´ una versi´n simplificada de la figura original. a o20.5. Construir un ´rbol de expresi´n a oEn esta secci´n analizamos expresiones infijas y formamos los correspondientes o´rboles de expresi´n. Por ejemplo, la expresi´n (3+7)*9 nos da el siguientea o o´rbol:a * + 9 3 7F´ ıjese en que hemos simplificando el diagrama omitiendo los nombres de losatributos.El analizador que vamos a escribir maneja expresiones que incluyen n´meros, upar´ntesis y los operadores + y *. Suponemos que la cadena de entrada ya ha esido tokenizada y almacenada en una lista de Python. La lista de tokens de(3+7)*9 es:
    • 20.5 Construir un ´rbol de expresi´n a o 217[’(’, 3, ’+’, 7, ’)’, ’*’, 9, ’fin’]El token fin es util para evitar que el analizador lea m´s all´ del final de la ´ a alista. A modo de ejercicio, escriba una funci´n que tome una cadena con- o teniendo una expresi´n y devuelva una lista de tokens. oLa primera funci´n que vamos a escribir es tomaToken, que toma como par´me- o atros una lista de tokens y el token que esperamos. Compara el token esperadocon el primer token de la lista: si coinciden, elimina el token de la lista y devuelveverdadero, en caso contrario, devuelve falso:def tomaToken(listaToken, esperado): if listaToken[0] == esperado: del listaToken[0] return 1 else: return 0Como listaToken apunta a un objeto mutable, los cambios hechos aqu´ son ıvisibles para cualquier otra variable que apunte al mismo objeto.La siguiente funci´n, obtieneNumero, maneja operandos. Si el siguiente token ode listaToken es un n´mero, obtieneNumero lo elimina y devuelve un nodo uhoja que contiene el n´mero; en caso contrario, devuelve None. udef obtieneNumero(listaToken): x = listaToken[0] if type(x) != type(0): return None del listaToken[0] return Arbol (x, None, None)Antes de seguir, debemos probar obtieneNumero aisladamente. Asignamos unalista de n´meros a listaToken, extraemos el primero, imprimimos el resultado ue imprimimos lo que quede de la lista de tokens:>>> listaToken = [9, 11, ’fin’]>>> x = obtieneNumero(listaToken)>>> imprimeArbolPostfijo(x)9>>> print listaToken[11, ’fin’]El siguiente m´todo que necesitamos es obtieneProducto, que construye un e´rbol de expresi´n para productos. Un producto simple tiene dos n´meros comoa o uoperandos, como 3 * 7.
    • 218 ´ ArbolesAqu´ tenemos una versi´n de obtieneProducto que maneja productos simples. ı odef obtieneProducto(listaToken): a = obtieneNumero(listaToken) if tomaToken(listaToken, ’*’): b = obtieneNumero(listaToken) return Arbol (’*’, a, b) else: return aSuponiendo que obtieneNumero se ejecuta con ´xito y devuelve un ´rbol simple, e aasignamos el primer operando a a. Si el siguiente car´cter es *, obtenemos el asegundo n´mero y construimos un ´rbol de expresi´n con a, b, y el operador. u a oSi el siguiente car´cter es cualquier otra cosa, simplemente devolvemos el nodo ahoja con a. Veamos dos ejemplos:>>> listaToken = [9, ’*’, 11, ’fin’]>>> arbol = obtieneProducto(listaToken)>>> imprimeArbolPostfijo(arbol)9 11 *>>> listaToken = [9, ’+’, 11, ’fin’]>>> arbol = obtieneProducto(listaToken)>>> imprimeArbolPostfijo(arbol)9El segundo ejemplo implica que entendemos un operando suelto como un tipo deproducto. Esta definici´n de “producto” es contraria a la intuici´n, pero resulta o oser util. ´Ahora tenemos que v´rnoslas con productos compuestos, como 3 * 5 * 13. eTratamos esta expresi´n como un producto de productos, es decir, 3 * (5 * o13). El ´rbol resultante es: a * 3 * 5 13Con un peque˜o cambio en obtieneProducto podemos manejar productos ar- nbitrariamente largos:
    • 20.5 Construir un ´rbol de expresi´n a o 219def obtieneProducto(listaToken): a = obtieneNumero(listaToken) if tomaToken(listaToken, ’*’): b = obtieneProducto(listaToken) # cambiamos esta l´nea ı return Arbol (’*’, a, b) else: return aEn otras palabras, un producto puede ser bien un operando aislado o un ´rbol acon * en su ra´ un n´mero en la derecha y un producto en la izquierda. Este ız, utipo de definici´n recursiva deber´ empezar a resultar familiar. o ıaComprobemos la nueva versi´n con un producto compuesto: o>>> listaToken = [2, ’*’, 3, ’*’, 5 , ’*’, 7, ’fin’]>>> arbol = obtieneProducto(listaToken)>>> imprimeArbolPostfijo(arbol)2 3 5 7 * * *Ahora a˜adiremos la capacidad de analizar sumas. De nuevo, usamos una defini- nci´n poco intuitiva de “suma”. Para nosotros, una suma puede ser un ´rbol con o a+ en la ra´ un producto en la izquierda y una suma en la derecha. O tambi´n, ız, euna suma puede ser simplemente un producto.Si quiere seguir adelante con esta definici´n, tiene una bonita propiedad: pode- omos representar cualquier expresi´n (sin par´ntesis) como una suma de produc- o etos. Esta propiedad es la base de nuestro algoritmo de an´lisis. aobtieneSuma intenta construir un ´rbol con un producto en la izquierda y una asuma en la derecha. Pero si no encuentra un +, simplemente construye un pro-ducto.def obtieneSuma(listaToken): a = obtieneProducto(listaToken) if tomaToken(listaToken, ’+’): b = obtieneSuma(listaToken) return Arbol (’+’, a, b) else: return aVamos a probarla con 9 * 11 + 5 * 7:>>> listaToken = [9, ’*’, 11, ’+’, 5, ’*’, 7, ’fin’]>>> arbol = obtieneSuma(listaToken)>>> imprimeArbolPostfijo(arbol)9 11 * 5 7 * +
    • 220 ´ ArbolesYa casi hemos acabado, pero todav´ tenemos que manejar los par´ntesis. ıa eEn culaquier lugar de una expresi´n donde puede haber un n´mero, puede o uhaber tambi´n una suma entera entre par´ntesis. S´lo necesitamos modificar e e oobtieneNumero para manejar subexpresiones:def obtieneNumero(listaToken): if tomaToken(listaToken, ’(’): x = obtieneSuma(listaToken) # obtiene la subexpresi´n o tomaToken(listaToken, ’)’) # quita el cierre de par´ntesis e return x else: x = listaToken[0] if type(x) != type(0): return None listaToken[0:1] = [] return Arbol (x, None, None)Vamos a probar este c´digo con 9 * (11 + 5) * 7: o>>> listaToken = [9, ’*’, ’(’, 11, ’+’, 5, ’)’, ’*’, 7, ’fin’]>>> arbol = obtieneSuma(listaToken)>>> imprimeArbolPostfijo(arbol)9 11 5 + 7 * *El analizador manej´ correctamente los par´ntesis; la suma ocurre antes de la o emultiplicaci´n. oEn la versi´n final del programa, ser´ bueno dar a obtieneNumero un nombre o ıam´s descriptivo de su nueva funci´n. a o20.6. Manejar erroresEn todo momento hemos supuesto que las expresiones estaban bien formadas.Por ejemplo, cuando alcanzamos el final de una subexpresi´n, suponemos que oel car´cter siguiente es un par´ntesis cerrado. Si hay un error y el siguiente a ecar´cter es cualquier otra cosa, deber´ a ıamos ocuparnos de ´l. edef obtieneNumero(listaToken): if tomaToken(listaToken, ’(’): x = obtieneSuma(listaToken) if not tomaToken(listaToken, ’)’): raise ’ExpresionErronea’, ’falta un par´ntesis’ e return x
    • 20.7 El ´rbol de animales a 221 else: # omitido el resto de la funci´n oLa sentencia raise crea una excepci´n; en este caso creamos un nuevo tipo de ex- ocepci´n, llamado ExpresionErronea. Si la funci´n que llam´ a obtieneNumero, o o oo alguna de las otras funciones de la pila de llamadas, maneja la expresi´n, el oprograma podr´ continuar. En caso contrario, Python imprimir´ un mensaje de a aerror y saldr´. a Como ejercicio, encuentre otros lugares de estas funciones donde puedan ocurrir errores y a˜ada las sentencias raise adecuadas. n Pruebe su c´digo con expresiones formadas incorrectamente. o20.7. El ´rbol de animales aEn esta secci´n desarrollaremos un peque˜o programa que usa un ´rbol para o n arepresentar una base de conocimientos.El programa interact´a con el usuario para crear un ´rbol de preguntas y nom- u abres de animales. Aqu´ tenemos un ejemplo de ejecuci´n: ı oEst´s pensando en un animal? s aEs un p´jaro? n aC´mo se llama el animal? perro oQu´ pregunta distinguir´a a un perro de un p´jaro? Puede volar e ı aSi el animal fuera un perro, cu´l ser´a la respuesta? n a ıEst´s pensando en un animal? s aPuede volar? nEs un perro? nC´mo se llama el animal? gato oQu´ pregunta distinguir´a a un gato de un perro? Ladra e ıSi el animal fuera un gato, cu´l ser´a la respuesta? n a ıEst´s pensando en un animal? s aPuede volar? nLadra? sEs un perro? sSoy el m´s grande! a
    • 222 ´ ArbolesEst´s pensando en un animal? n aEste es el ´rbol que construye este di´logo: a a Can it fly? n y Does it bark? bird n y cat dogAl principio de cada ronda, el programa empieza en lo alto del ´rbol y hace la aprimera pregunta. Dependiendo de la respuesta, se mueve al hijo de la izquierdao de la derecha y sigue hasta que llega a un nodo hoja. En ese momento, intentaadivinar. Si falla, pide al usuario el nombre del nuevo animal y una pregunta quedistinga al intento fallido del nuevo animal. Entonces a˜ade un nodo al ´rbol n acon la nueva pregunta y el nuevo animal.´Este es el c´digo: odef animal(): # empezar con un nodo suelto raiz = Arbol("p´jaro") a # bucle hasta que el usuario salga while 1: print if not si("Est´s pensando en un animal? "): break a # recorrer el ´rbol a arbol = raiz while arbol.tomaIzquierda() != None: indicador = arbol.tomaCarga() + "? " if si(indicador): arbol = arbol.tomaDerecha() else: arbol = arbol.tomaIzquierda() # intentar adivinar
    • 20.7 El ´rbol de animales a 223 adivina = arbol.tomaCarga() indicador = "Es un " + adivina + "? " if si(indicador): print "Soy el m´s grande!" a continue # obtener informaci´n nueva o indicador = "C´mo se llama el animal? " o animal = raw_input(indicador) indicador = "Qu´ pregunta distinguir´a a un %s de un %s? " e ı pregunta = raw_input(indicador % (animal,adivina)) # a~adir informaci´n nueva al ´rbol n o a arbol.ponCarga(pregunta) indicador = "Si el animal fuera un %s, cu´l ser´a la a ı respuesta? " if si(indicador % animal): arbol.ponIzquierda(Arbol(adivina)) arbol.ponDerecha(Arbol(animal)) else: arbol.ponIzquierda(Arbol(animal)) arbol.ponDerecha(Arbol(adivina))La funci´n si es un auxiliar; imprime un indicador y acepta una entrada del ousuario. Si la respuesta comienza con s o S, la funci´n devuelve verdadero: odef si(preg): from string import lower resp = lower(raw_input(preg)) return (resp[0] == ’s’)La condici´n del bucle externo es 1, lo que significa que seguir´ hasta que se o aejecute la sentencia break cuando el usuario ya no piense en ning´n animal. uEl bucle while interno recorre el ´rbol de arriba a abajo, guiado por las res- apuestas del usuario.Cuando se a˜ade un nuevo nodo al ´rbol, la pregunta sustituye a la carga y los n ados hijos son el animal nuevo y la carga original.Una carencia del programa es que, al salir, ¡olvida todo lo que usted le hab´ ıaense˜ado con tanto cuidado! n Como ejercicio, piense en varias formas en las que podr´ guardar el ıa arbol de conocimiento en un archivo. Implemente la que piense que ´ es m´s f´cil. a a
    • 224 ´ Arboles20.8. Glosario´rbol binario: Un ´rbol en el que cada nodo apunta a cero, uno, o dos nodosa a dependientes.ra´ El nodo superior de un ´rbol, sin padre. ız: ahoja: Un nodo del extremo inferior de un ´rbol, sin hijos. apadre: El nodo que apunta a un nodo dado.hijo: Uno de los nodos a los que apunta un nodo.hermanos: Nodos que tienen un padre com´n. univel: El conjunto de nodos equidistante de la ra´ ız.operador binario: Un operador que toma dos operandos.subexpresi´n: Una expresi´n entre par´ntesis que act´a como un operando o o e u simple dentro de otra expresi´n mayor. oorden prefijo: Una forma de recorrer un ´rbol, visitando cada nodo antes que a a sus hijos.notaci´n prefija: Una forma de escribir una expresi´n matem´tica en la que o o a los operadores aparecen antes que sus operandos.orden postfijo: Una forma de recorrer un ´rbol, visitando los hijos de cada a nodo antes del propio nodo.orden infijo: Una forma de recorrer un ´rbol, visitando el sub´rbol izquierdo, a a luego la ra´ y luego el sub´rbol derecho. ız, a
    • Ap´ndice A eDepuraci´n oEn un programa pueden suceder varios tipos de error, y resulta util distinguirlos ´para localizarlos r´pidamente: a Python presenta errores de sintaxis mientras traduce el c´digo fuente en o c´digo binario. Normalmente indican que hay algo err´neo en la sintaxis o o del programa. Ejemplo: omitir los dos puntos al final de una sentencia def nos da el mensaje SyntaxError: invalid syntax, algo redundante. El sistema de tiempo de ejecuci´n presenta los errores en tiempo de eje- o cuci´n si algo va mal mientras se ejecuta el programa. La mayor´ de los o ıa mensajes de error en tiempo de ejecuci´n incluyen informaci´n acerca de o o d´nde sucedi´ el error y qu´ funciones se estaban ejecutando. Ejemplo: o o e una recursi´n infinita termina por provocar un error en tiempo de ejecu- o ci´n del “maximum recursion depth exceeded” (superada la profundidad o m´xima de recursi´n). a o Los errores sem´nticos son problemas con un programa que compila y se a ejecuta pero no hace lo que se espera de ´l. Ejemplo: una expresi´n puede e o no evaluarse en el orden esperado, dando un resultado inesperado.El primer paso de la depuraci´n es averiguar con qu´ tipo de error se enfrenta. o eAunque las secciones que siguen est´n organizadas por tipos de error, algunas at´cnicas son aplicables en m´s de una situaci´n. e a oA.1. Errores de sintaxisLos errores de sintaxis suelen ser f´ciles de arreglar una vez que averigua alo que son. Desgraciadamente, muchas veces los mensajes de error no son
    • 226 Depuraci´n omuy utiles. Los mensajes m´s comunes son SyntaxError: invalid syntax y ´ aSyntaxError: invalid token, ninguno de los cuales es muy informativo.Por otra parte, el mensaje le dice en qu´ lugar del programa sucedi´ el error. e oEn realidad, le dice d´nde not´ el problema Python, que no es necesariamente o odonde est´ el error. A veces el error est´ antes de la localizaci´n del mensaje de a a oerror, muchas veces en la l´ ınea anterior.Si est´ haciendo el programa incrementalmente, deber´ tener casi localizado el a ıaerror. Estar´ en la ultima l´ a ´ ınea que a˜adi´. n oSi est´ usted copiando c´digo de un libro, comience comparando con atenci´n a o osu c´digo con el del libro. Compruebe cada car´cter. Al mismo tiempo, recuerde o aque el libro podr´ estar equivocado, as´ que si ve algo que parezca un error de ıa ısintaxis, podr´ serlo. ıaHe aqu´ algunas formas de evitar los errores de sintaxis m´s habituales: ı a 1. Aseg´rese de que no utiliza una palabra clave de Python como nombre de u variable. 2. Compruebe que tiene los dos puntos al final de la cabecera de todas las sentencias compuestas, las for, while, if, y def. 3. Compruebe que el sangrado es consistente. Puede usted sangrar tanto con espacios como con tabuladores, pero es mejor no mezclarlos. Todos los niveles deber´ estar anidados por la misma cantidad. ıan 4. Aseg´rese de que todas las cadenas del c´digo tienen su par de comillas u o de apertura y cierre. 5. Si tiene cadenas que ocupan varias l´ ıneas con triples comillas (o triples ap´strofos), aseg´rese de que ha terminado la cadena correctamente. Una o u cadena sin terminar puede provocar un error invalid token 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, podr´ıa no presentar ning´n mensaje de error! u 6. Un par´ntesis sin cerrar—(, { o [—hace que Python continue con la l´ e ınea siguiente como parte de la sentencia actual. Generalmente aparecer´ un a error casi inmediatamente en la l´ ınea siguiente. 7. Compruebe el cl´sico = donde deber´ haber un == en los condicionales. a ıaSi nada funciona, siga con la secci´n que sigue... o
    • A.2 Errores en tiempo de ejecuci´n o 227A.1.1. No consigo ejecutar mi programa, no importa lo que haga.Si el compilador dice que hay un error pero usted no lo ve, podr´ ser porque ıausted y el compilador no miran el mismo c´digo. Compruebe su entorno de oprogramaci´n para asegurarse de que el programa que est´ editando es el que o aest´ intentando ejecutar Python. Si no est´ seguro, pruebe a poner un error de a asintaxis obvio y deliberado al principio del programa. Ahora ejecute (o importe)de nuevo. Si el compilador no encuentra el nuevo error probalemente hay algoequivocado en el modo en que est´ configurado su entorno. aSi esto ocurre, puede enfrentarse a ello empezando de nuevo con un programanuevo como “Hola, mundo”, y asegurarse de que puede hacer que funcione unprograma conocido. Luego a˜ada gradualmente los trozos del programa nuevo nal que funciona.A.2. Errores en tiempo de ejecuci´n oUna vez que su programa es sint´cticamente correcto, Python pude importarlo ay al menos comenzar a ejecutarlo. ¿Qu´ podr´ ir mal? e ıaA.2.1. Mi programa no hace nada de nada.Este problema es muy com´n cuando su archivo consta de funciones y clases upero en realidad no invoca nada para que empiece la ejecuci´n. Esto puede ser ointencionado cuando s´lo planea importar el m´dulo para suministrar clases y o ofunciones.Sin no es intencionado, aseg´rese de que est´ llamando a una funci´n que inicie u a ola ejecuci´n, o ejecute una desde el indicador interactivo. Vea tambi´n la secci´n o e o“Flujo de Ejecuci´n” m´s adelante. o aA.2.2. Mi programa se cuelga.Si un programa se para y parece no hacer nada, decimos que “se ha colgado”.A menudo significa que se ha quedado atrapado en un bucle infinito o en unarecursi´n infinita. o Si hay un bucle en particular que le resulta sospechoso de provocar el problema, a˜ada una sentencia print justo antes del bucle que diga “en- n trando al bucle” y otra inmediatamente despu´s que diga “saliendo del e bucle”.
    • 228 Depuraci´n o Ejecute el programa. Si obtiene el primer mensaje pero el segundo no, tiene usted un bucle infinito. Vaya a la secci´n “Bucle Infinito” m´s adelante. o a Una recursi´n infinita casi siempre har´ que el programa corra un rato o a y luego presente un error de “RuntimeError: Maximum recursion depth exceeded”. Si ocurre eso, vaya a la secci´n “Recursi´n Infinita” m´s ade- o o a lante. Si no ve este error pero sospecha que hay un problema con un m´todo o e funci´n recursivos tambi´n puede utilizar las t´cnicas de la secci´n “Re- o e e o cursi´n Infinita”. o Si no funciona ninguno de estos pasos, comience a probar otros bucles y otros m´todos y funciones recursivos. e Si eso no funciona, es posible que no comprenda el flujo de ejecuci´n de o su programa. Vaya a la secci´n “Flujo de Ejecuci´n” m´s adelante. o o aBucle InfinitoSi cree que tiene un bucle infinito y piensa que sabe qu´ bucle provoca el pro- eblema, a˜ada una sentencia print que imprima los valores de las variables de nla condici´n al final del bucle junto con el valor de la condici´n. o oPor ejamplo:while x > 0 and y < 0 : # hacer algo con x # hacer algo con y print "x: ", x print "y: ", y print "condici´n: ", (x > 0 and y < 0) oAhora, cuando ejecute el programa, ver´ tres l´ a ıneas de salida en cada vuelta delbucle. En la ultima vuelta el valor de la condici´n deber´ ser false. Si el bucle ´ o ıasigue ejecut´ndose, podr´ ver los valores de x e y, y podr´ averiguar por qu´ no a a a ese actualizan correctamente.Recursi´n Infinita oUna recursi´n infinita casi siempre har´ que el programa se ejecute un rato y o aluego provoque un error de Maximum recursion depth exceeded.Si sospecha que una funci´n o un m´todo est´ causando una recursi´n infinita, o e a ocomience por asegurarse de que hay un caso b´sico. En otras palabras, deber´ a ıa
    • A.2 Errores en tiempo de ejecuci´n o 229haber una condici´n que haga que la funci´n devuelva un valor sin hacer otra o ollamada recursiva. Si no, necesita revisar el algoritmo y encontrar ese caso b´sico. aSi hay un caso b´sico pero el programa no parece llegar hasta ´l, a˜ada una a e nsentencia print que imprima los par´metros al principio de la funci´n o m´todo. a o eCuando ahora ejecute el programa, ver´ unas pocas l´ a ıneas cada vez que seinvoque la funci´n o m´todo y all´ ver´ los par´metros. Si los par´metros no se o e ı a a aacercan al caso b´sico, eso le dar´ alguna idea de por qu´ no lo hace. a a eFlujo de Ejecuci´n oSi no est´ seguro de qu´ curso sigue el flujo de ejecuci´n en su programa, a˜ada a e o nsentencias print al principio de cada funci´n con un mensaje como “entrando oen la funci´n turur´”, donde turur´ es el nombre de la funci´n. o u u oCuando ahora ejecute el programa, imprimir´ una traza de cada funci´n a me- a odida que las vaya invocando.A.2.3. Cuando ejecuto el programa recibo una excepci´n. oSi algo va mal durante la ejecuci´n, Python imprime un mensaje que incluye el onombre de la excepci´n, la l´ o ınea del programa donde sucedi´ el problema y una otraza inversa.La traza inversa identifica la funci´n que se est´ ejecutando ahora y la funci´n o a oque invoc´ a ´sta, y luego la funci´n que invoc´ a ´sa, y as´ sucesivamente. En o e o o e ıotras palabras, traza la ruta de las llamadas a las funciones que le llevaron adonde se encuentra. Tambi´n incluye los n´meros de las l´ e u ıneas de sus archivosdonde suceden todas esas llamadas.El primer paso es examinar el lugar del programa donde sucede el error y versi puede adivinar lo que sucedi´. Estos son algunos de los errores en tiempo de oejecuci´n m´s comunes: o aNameError: Est´ intentando usar una variable que no existe en el entorno a actual. Recuerde que las variables locales son locales. No puede hacer referencia a ellas desde fuera de la funci´n en la que se definen. oTypeError: Hay varias causas posibles: Est´ intentando usar un varlor de forma inadecuada. Ejemplo: usar a como ´ındice para una cadena, lista o tupla algo que no es un entero.
    • 230 Depuraci´n o Hay una discrepancia entre los elementos de una cadena de formato y los elementos pasados para la conversi´n. Esto puede ocurrir tanto si o el n´mero de elementos no coincide como si se solicita una conversi´n u o no v´lida. a Est´ pasando un n´mero err´neo de argumentos a una funci´n o a u o o m´todo. Con los m´todos, f´ e e ıjese en la definici´n de los m´todos y o e compruebe que el primer par´metro es self. Luego f´ a ıjese en la invo- caci´n del m´todo; aseg´rese de que est´ invocando el m´todo sobre o e u a e un objeto del tipo adecuado y d´ndole correctamente el resto de ar- a gumentos.KeyError: Est´ tratando de acceder a un elemento de un diccionario con una a clave que no est´ en el diccionario. aAttributeError: Est´ intentando acceder a un atributo o m´todo que no exis- a e te.IndexError: El ´ındice que est´ usando para acceder a una lista, cadena o tupla a es mayor que su longitud menos uno. Justo antes de donde aparece el error, a˜ada una sentencia print que muestre el valor del ´ n ındice y la longitud del vector. ¿Es correcto el tama˜o del vector? ¿Tiene el ´ n ındice un valor correcto?A.2.4. Puse tantas sentencias print que me ahoga la sali- da.Uno de los problemas de usar sentencias print para la depuraci´n es que pue- ode terminar enterrado en informaci´n. Hay dos formas de atajar el problema: osimplificar la salida o simplificar el programa.Para simplificar la salida, puede elimiar o comentar (convertir en comentarios)las sentencias print que no sean de ayuda, o combinarlas, o dar a la salida unformato que la haga m´s comprensible. aPara simplificar el programa puede hacer varias cosas. Primero, reducir la escaladel problema en el que est´ trabajando el programa. Por ejemplo, si est´ orde- a anando un vector, ordene un vector peque˜o. Si el programa acepta entradas del nusuario, dele la entrada m´s simple que provoque el problema. aSegundo, limpie el programa. Elimine el c´digo muerto y reorganice el programa opara hacerlo tan legible como sea posible. Por ejemplo, si sospecha que el proble-ma est´ en una parte del programa con un anidamiento muy profundo, pruebe aa reescribir esa parte con una estructura m´s simple. Si sospecha de una funci´n a ogrande, trate de trocearla en funciones menores y pru´belas separadamente. e
    • A.3 Errores sem´nticos a 231El proceso de encontrar el caso m´ınimo de prueba le llevar´ a menudo al error. aSi se encuentra con que un programa funciona en una situaci´n pero no en otra, oeso le dar´ una pista sobre lo que ocurre. aDe forma parecida, la reescritura de una porci´n de c´digo puede ayudarle a o oencontrar errores sutiles. Si hace un cambio que a usted le parece que no afectaal programa, pero s´ lo hace, le dar´ una pista. ı aA.3. Errores sem´nticos aEn cierto modo, los errores sem´nticos son los m´s dif´ a a ıciles de corregir, porqueel compilador y el sistema de ejecuci´n no proporcionan informaci´n sobre lo o oque va mal. S´lo usted sabe lo que se supone que debe hacer el programa, y s´lo o ousted sabe que no lo est´ haciendo. aEl primer paso es hacer una concexi´n entre el texto del programa y el compor- otamiento que est´ usted viendo. Necesita una hip´tesis sobre lo que realmente a oest´ haciendo el programa. Una de las dificultades que nos encontramos para aello es la alta velocidad de los computadores.A menudo desear´ ralentizar el progrma a una velocidad humana, y con al- ıagunos programas depuradores podr´ hacerlo. Pero el tiempo que lleva colocar aunas sentencias print en los lugares adecuadoes suele ser menor que el quelleva configurar el depurador, poner y quitar puntos de interrupci´n y “hacer ocaminar” el programa hasta donde se produce el error.A.3.1. Mi programa no funciona.Deber´ hacerse estas preguntas: ıa ¿Hay algo que se supone que deber´ hacer el programa pero que no parece ıa suceder? Busque la secci´n del c´digo que realiza esa funci´n y aseg´rese o o o u de que se ejecuta cuando deber´ıa. ¿Ocurre algo que no deber´ Busque el programa que realiza esa funci´n ıa? o y vea si se ejecuta cuando no debe. ¿Hay una secci´n de c´digo que causa un efecto que no esperaba? aseg´rese o o u de que entiende el c´digo en cuesti´n, especialmente si incluye invocaciones o o de funciones o m´todos de otros m´dulos de Python. Lea la documentaci´n e o o de las funciones que invoca. Pru´belas escribiendo casos de prueba simples e y comprobando el resultado.
    • 232 Depuraci´n oPara programar necesitar´ tener un modelo mental de c´mo funcionan los pro- a ogramas. Si escribe un programa que no hace lo que espera de ´l, muchas veces eel problema no estar´ en el programa, sino en su modelo mental. aLa mejor manera de corregir su modelo mental es dividiendo el programa en suscomponentes (normalmente las funciones y m´todos) y probando cada compo- enente de forma independiente. Una vez que encuentre la discrepancia entre sumodelo y la realidad, podr´ solucionar el problema. aPor supuesto, deber´ ir haciendo y probando componentes tal como desarrolla ıael programa. Si encuentra un problema, s´lo habr´ una peque˜a cantidad de o a nc´digo nuevo del que no sabe si est´ correcto. o aA.3.2. Tengo una expresi´n grande y peliaguda y no hace o lo que espero.Est´ bien escribir expresi´n complejas mientras sean legibles, pero pueden ser a odif´ ıciles de depurar. Suele ser una buena idea dividir una expesi´n compleja en ouna serie de asignaciones de variables temporales.Por ejamplo:self.manos[i].agregaCarta (self.manos[ self.encuentraVecino(i)].darCarta())Puede reescribirse como:vecino = self.encuentraVecino (i)cartaElegida = self.manos[vecino].darCarta()self.manos[i].agregaCarta (cartaElegida)La versi´n expl´ o ıcita es m´s f´cil de leer porque los nombres de variable nos facili- a atan documentaci´n adicional, y es m´s f´cil de depurar porque puede comprobar o a alos tipos de las variables intermedias y mostrar sus valores.Otro problema que puede suceder con las expresiones grandes es que el orden deevaluaci´n puede no ser el que usted esperaba. Por ejemplo, si est´ traduciendo o ala expresi´n 2π a Python, podr´ escribir: o x ıay = x / 2 * math.pi;Eso no es correcto, porque la multiplicaci´n y la divisi´n tienen la misma prece- o odencia y se eval´an de izquierd a derecha. As´ que esa expresi´n calcula xπ/2. u ı oUna buena forma de depurar expresiones es a˜adir par´ntesis para hacer expl´ n e ıci-to el orden de evaluaci´n: o
    • A.3 Errores sem´nticos a 233 y = x / (2 * math.pi);Siempre que no est´ seguro del orden de evaluaci´n, utilice par´ntesis. El pro- e o egrama no s´lo ser´ correcto (en el sentido de hacer lo que usted prentend´ o a ıa),sino que adem´s ser´ m´s legible para otras personas que no hayan memorizado a a alas reglas de precedencia.A.3.3. Tengo una funci´n o m´todo que no devuelve lo o e que esperaba.Si tiene una sentencia return con una expresi´n compleja no tendr´ la opor- o atunidad de imprimir el valor de retorno antes de volver. De nuevo, puede usaruna variable temporal. Por ejemplo, en lugar de:return self.manos[i].eliminaCoincidencias()podr´ excribir: ıacant = self.manos[i].eliminaCoincidencias()return cantAhora ya tiene la oportunidad de mostrar el valor de cant antes de regresar.A.3.4. Estoy atascado de verdad y necesito ayuda.Primero, intente alejarse del computador durante unos minutos. Los computa-dores emiten unas ondas que afectan al cerebro provocando estos efectos: Frustraci´n y/o furia. o Creencias supersticiosas (“el computador me odia”) y pensamiento m´gico a (“el programa s´lo funciona cuando me pongo la gorra hacia atr´s”). o a Programar dando palos de ciego (el empe˜o de programar escribiendo n todos los programas posibles y eligiendo el que hace lo correcto).Si se encuentra afectado por alguno de estos s´ ıntomas, lev´ntese y d´ un paseo. a eCuando est´ calmado, piense en el programa. ¿Qu´ es lo que hace? ¿Cu´les e e apueden ser las causas de tal comportamiento? ¿Cu´ndo fue la ultima vez que a ´ten´ un programa que funcinaba y qu´ fue lo siguiente que hizo? ıa eA veces lleva tiempo encontrar un error. Muchas veces encontramos errorescuando estamos lejos del computador y divagamos. Algunos de los mejores lu-gares para encontrar errores son los trenes, las duchas y la cma, justo antes dequedarse dormido.
    • 234 Depuraci´n oA.3.5. No, de verdad necesito ayuda.Sucede. Incluso los mejores programadores se atascan de vez en cuando. A vecestrabaja durante tanto tiempo en un programa que no puede ver el error. Lo quenecesita es un par de ojos nuevos.Antes de llamar a andie, aseg´rese de que ha agotado las t´cnicas explicadas u eaqu´ Su programa deber´ ser tan simple como sea posible, y usted deber´ estar ı. ıa ıatrabajando con la entrada m´ ınima que provoca el error. Deber´ tener sentencias ıaprint en los lugares adecuados (y lo que dicen deber´ ser comprensible). De- ıaber´ entender el problema lo bastante bien como para describirlo sucintamente. ıaCuando llame a alguien para que le ayude, aseg´rese de darles la informaci´n u oque necesitan: Si hay un mensaje de error, ¿cu´l es y qu´ parte del programa se˜ala? a e n ¿Qu´ fue lo ultimo que hizo antes de que apareciera el error? ¿Cu´les son e ´ a las ultimas l´ ´ ıneas de c´digo que escribi´, o cu´l es el nuevo caso de prueba o o a que no cumple? ¿Qu´ ha intentado hasta ahora y qu´ ha averiguado? e eCuando encuentre el error, t´mese un momento para pensar acerca de lo que opodr´ haber hecho para encontrarlo m´s r´pido. La siguiente vez que vea algo ıa a aparecido, ser´ capaz de encontrar el error antes. aRecuerde, el objetivo no es s´lo hacer que el programa funciones. El objetivo es oaprender c´mo hacer funcionar al programa. o
    • Ap´ndice B eCrear un nuevo tipo dedatosLos lenguajes de programaci´n orientados a objetos permiten a los programado- ores crear nuevos tipos de datos que se comporten de manera muy parecida a lostipos de datos nativos. Exploraremos esta posibilidad construyendo una claseFraccion que funcione de manera muy similar a los tipos num´ricos nativos, eenteros, enteros largos y flotantes.Las fracciones, tambi´n conocidas como n´meros racionales, son valores que e upueden expresrse como la proporci´n entre dos n´meros enteros, tal como 5/6. o uAl n´mero superior se se le llama numerador y al inferior se se le llama deno- uminador.Comenzamos definiendo la clase Fraccion con un m´todo de inicializaci´n que e onos surta de un numerador y un demonimador enteros:class Fraccion: def __init__(self, numerador, denominador=1): self.numerador = numerador self.denominador = denominadorEl denominador es opcional. Una Fraccion con un s´lo par´metro representa un o an´mero entero. Si el numerador es n, construimos la fracci´n n/1. u oEl siguente paso es escribir un m´todo str para que imprima las frac- eciones de forma que tenga sentido. La forma natural de hacerlo es “numera-dor/denominador”:
    • 236 Crear un nuevo tipo de datosclass Fraccion: ... def __str__(self): return "%d/%d" % (self.numerador, self.denominador)Para probar lo que tenemos hasta ahora, lo ponemos en un fichero llamadoFraccion.py y lo importamos desde el int´rprete de Python. Entonces creamos eun objeto fracci´n y lo imprimimos. o>>> from Fraccion import fraccion>>> mortadela = Fraccion(5,6)>>> print "La fracci´n es", mortadela oLa fracci´n es 5/6 oComo siempre, la funci´n print invoca impl´ o ıcitamente al m´todo e str .B.1. Multiplicaci´n de fracciones oNos gustar´ poder aplicar las operaciones normales de suma, resta, multiplica- ıaci´n y divisi´n a las fracciones. Para ello, podemos sobrecargar los operadores o omatem´ticos para los objetos de clase Fraccion. aComenzaremos con la multiplicaci´n porque es la m´s f´cil de implementar. o a aPara multiplicar dos fraciones, creamos una nueva fracci´n cuyo numerador oes el producto de los numeradores de los operandos y cuyo denominador esel producto de los denominadores de los operandos. mul es el nombre quePython utiliza para el m´todo que sobrecarga al operador *: eclass Fraccion: ... def __mul__(self, otro): return Fraccion(self.numerador*otro.numerador, self.denominador*otro.denominador)Podemos probar este m´todo calculando el producto de dos fracciones: e>>> print Fraccion(5,6) * Fraccion(3,4)15/24Funciona, pero ¡podemos hacerlo mejor! Podemos ampliar el m´todo para ma- enejar la multiplicaci´n por un entero. Usamos la funci´n type para ver si otro o oes un entero y convertirlo en una fracci´n en tal caso. oclass Fraccion: ...
    • B.2 Suma de fracciones 237 def __mul__(self, otro): if type(otro) == type(5): otro = Fraccion(otro) return Fraccion(self.numerador * otro.numerador, self.denominador * otro.denominador)Ahora funciona la multiplicaci´n para fracciones y enteros, pero s´lo si la frac- o oci´n es el operando de la izquierda. o>>> print Fraccion(5,6) * 420/6>>> print 4 * Fraccion(5,6)TypeError: __mul__ nor __rmul__ defined for these operandsPara evaluar un operador binario como la multiplicaci´n, Python comprueba oprimero el operando de la izquierda para ver si proporciona un m´todo mul eque soporte el tipo del segundo operando. En este caso, el operador nativo demultiplicaci´n del entero no soporta fracciones. oDespu´s, Python comprueba el segundo operando para ver si provee un m´todo e e rmul que soporte el tipo del primer operando. En este caso, no hemos provistoel m´todo rmul , por lo que falla. ePor otra parte, hay una forma sencilla de obtener rmul :class Fraccion: ... __rmul__ = __mul__Esta asignaci´n hace que el m´todo rmul sea el mismo que mul . Si ahora o eevaluamos 4 * Fraccion(5,6), Python llamar´ al m´todo rmul del objeto a eFraccion y le pasar´ 4 como par´metro: a a>>> print 4 * Fraccion(5,6)20/6Dado que rmul es lo mismo que mul , y mul puede manejar un par´me- atro entero, ya est´ hecho. aB.2. Suma de fraccionesLa suma es m´s complicada que la multiplicaci´n, pero a´n es llevadera. La a o usuma de a/b y c/d es la fracci´n (a*d+c*b)/b*d. oUsando como modelo el c´digo de la multiplicaci´n, podemos escribir o o add y radd :
    • 238 Crear un nuevo tipo de datosclass Fraccion: ... def __add__(self, otro): if type(otro) == type(5): otro = Fraccion(otro) return Fraccion(self.numerador * otro.denominador + self.denominador * otro.numerador, self.denominador * otro.denominador) __radd__ = __add__Podemos probar estos m´todos con Fracciones y enteros. e>>> print Fraccion(5,6) + Fraccion(5,6)60/36>>> print Fraccion(5,6) + 323/6>>> print 2 + Fraccion(5,6)17/6Los dos primeros ejemplos llaman a add ; el ultimo llama a ´ radd .B.3. Algoritmo de EuclidesEn el ejemplo anterior, computamos la suma de 5/6 + 5/6 y obtuvimos 60/36.Es correcto, pero no es la mejor forma de representar la respuesta. Para redu-cir la fracci´n a su expresi´n m´s simple, hemos de dividir el numerador y el o o adenominador por el m´ximo com´ n divisor (MCD) de ambos, que es 12. a uEl resultado ser´ 5/3. ıaEn general, siempre que creamos un nuevo objeto Fraccion, deber´ ıamos redu-cirlo dividiendo el numerador y el denominador por el MCD de ambos. Si lafracci´n ya est´ reducida, el MCD es 1. o aEuclides de Alejandr´ (aprox. 325–265 a. C.) prensent´ un algoritmo para en- ıa ocontrar el MCD de dos n´meros entermos m y n: u Si n divide a m sin resto, entonces n es el MCD. De lo contrario, el MCD es el MCD de n y el resto de dividir m entre n.Esta definici´n recursiva puede expresarse concisamente como una funci´n: o odef mcd (m, n): if m % n == 0:
    • B.4 Comparar fracciones 239 return n else: return mcd(n, m%n)En la primera l´ınea del cuerpo, usamos el operador de m´dulo para comprobar la odivisibilidad. En la ultima l´ ´ ınea, lo usamos para calcular el resto de la divisi´n. oDado que todas las operaciones que hemos escrito creaban un nuevo objetoFraccion para devolver el resultado, podemos reducir todos los resultados mo-dificando el m´todo de inicializaci´n. e oclass Fraccion: def __init__(self, numerador, denominador=1): m = mcd (numerador, denominador) self.numerador = numerador / m self.denominador = denominador / mAhora siempre que creemos una Fraccion quedar´ reducida a su forma can´ni- a oca:>>> Fraccion(100,-36)-25/9Una caracter´ ıstica estupenda de mcd es que si la fracci´n es negativa, el signo omenos siempre se trasladar´ al numerador. aB.4. Comparar fraccionesSupongamos que tenemos dos objetos Fraccion, a y b, y evaluamos a == b. Laimplemetaci´n por defecto de == comprueba la igualdad superficial, por lo que os´lo devuelve true si a y b son el mismo objeto. oQueremos m´s bien devolver verdadero si a y b tienen el mismo valor —eso es, aigualdad en profundidad.Hemos de ense˜ar a las fracciones c´mo compararse entre s´ Como vimos en la n o ı.Secci´n 15.4, podemos sobrecargar todos los operadores de comparaci´n de una o ovez proporcionando un m´todo cmp . ePor convenio, el m´todo cmp devuelve un n´mero negativo si self es menor e uque otro, zero si son lo mismo, y un n´mero positivo si self es mayor que otro. uLa forma m´s simple de comparar dos fracciones es la multipicaci´n cruzada. a oSi a/b > c/d, entonces ad > bc. Con esto en mente, aqu´ est´ el c´digo para ı a o cmp :
    • 240 Crear un nuevo tipo de datosclass Fraccion: ... def __cmp__(self, otro): dif = (self.numerador * otro.denominador - otro.numerador * self.denominador) return difSi self es mayor que otro, entonces dif ser´ positivo. Si otro is mayor, entonces adif ser´ ngativo. Si son iguales, dif es cero. aB.5. Forzando la m´quina aPor supuesto, a´n no hemos terminado. Todav´ hemos de implementar la resta u ıasobrecargando sub y la divisi´n sobrecargando div . oUna manera de manejar estas operaciones es implementar la negaci´n sobre- ocargando neg y la inversi´n sobrecargando invert . Entonces podemos orestar negando el segundo operando y sumando, y podemos dividir invirtiendoel segundo operando y multiplicando.Luego, hemos de suministrar los m´todos rsub y rdiv . Desgraciadamen- ete, no podemos usar el mismo truco que usamos para la suma y la multiplicaci´n, oporque la resta y la divisi´n no son conmutativas. No podemos igualar rsub oy rdiv a sub y div . En estas operaciones, el orden de los operandostiene importancia.Para manejar la negaci´n unitaria, que es el uso del signo menos con un unico o ´operando, sobrecargamos el m´todo neg . ePodemos computar potencias sobrecargando pow , pero la implementaci´n otiene truco. Si el exponente no es un n´mero entero podr´ no ser posible u ıarepresentar el resultado como una Fraccion. Por ejemplo, Fraccion(2) **Fraccion(1,2) es la raiz cuadrada de 2, que es un n´mero irracional (no se upuede representar como una fracci´n). Por lo tanto, no es f´cil escribir la ver- o asi´n m´s general de pow . o aExiste otra extensi´n a la clase Fraccion que cabr´ considerar. Hasta ahora, o ıahemos asumido que el numerador y el denominador son enteros. Podr´ ıamosconsiderar la posibilidad de pertimirles que sean enteros largos. Como ejercicio, termine la implementaci´n de la clase Fraccion de o forma que pueda manejar resta, divisi´n, exponenciaci´n y enteros o o largos como numerador y denominador.
    • B.6 Glosario 241B.6. Glosariom´ximo com´ n divisor (MCD): El mayor entero positivo que divide al nu- a u merador y al denominador de una fracci´n sin que quede un resto. oreducir: Cambiar la fracci´n a su forma equivalente con un MCD igual a 1. onegaci´n unitaria: Operaci´n que computa el elemento sim´trico aditivo, nor- o o e malmente denotada con un signo menos delante. Se denomina “unitaria” en contraste con la operaci´n binaria menos, que es la resta. o
    • Ap´ndice C eListados Completos dePythonC.1. Clase Puntoclass Punto: def __init__(self, x=0, y=0): self.x = x self.y = y def __str__(self): return ’(’ + str(self.x) + ’, ’ + str(self.y) + ’)’ def __add__(self, otro): return Punto(self.x + otro.x, self.y + otro.y) def __sub__(self, otro): return Punto(self.x - otro.x, self.y - otro.y) def __mul__(self, otro): return self.x * otro.x + self.y * otro.y def __rmul__(self, otro): return Punto(otro * self.x, otro * self.y) def reverse(self):
    • 244 Listados Completos de Python self.x, self.y = self.y, self.x def delDerechoYDelReves(derecho): from copy import copy reves = copy(derecho) reves.reverse() print str(derecho) + str(reves)C.2. Clase Horaclass Hora: def __init__(self, horas=0, minutos=0, segundos=0): self.horas = horas self.minutos = minutos self.segundos = segundos def __str__(self): return str(self.horas) + ":" + str(self.minutos) + ":" + str(self.segundos) def convierteASegundos(self): minutos = self.horas * 60 + self.minutos segundos = self.minutos * 60 + self.segundos return segundos def incrementa(self, segs): segs = segs + self.segundos self.horas = self.horas + segs/3600 segs = segs % 3600 self.minutos = self.minutos + segs/60 segs = segs % 60 self.segundos = segs def haceHora(segs): hora = Hora() hora.horas = segs/3600 segs = segs - hora.horas * 3600 hora.minutos = segs/60 segs = segs - hora.minutos * 60
    • C.3 Cartas, mazos y juegos 245 hora.segundos = segs return horaC.3. Cartas, mazos y juegosimport randomclass Carta: listaDePalos = ["Tr´boles", "Diamantes", "Corazones", e "Picas"] listaDeValores = ["nada", "As", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Sota", "Reina", "Rey"] def __init__(self, palo=0, valor=0): self.palo = palo self.valor = valor def __str__(self): return (self.listaDeValores[self.valor] + " de " + self.listaDePalos[self.palo]) def __cmp__(self, otro): # controlar el palo if self.palo > otro.palo: return 1 if self.palo < otro.palo: return -1 # si son del mismo palo, controlar el valor if self.valor > otro.valor: return 1 if self.valor < otro.valor: return -1 # los valores son iguales, es un empate return 0class Mazo: def __init__(self): self.cartas = [] for palo in range(4): for valor in range(1, 14): self.cartas.append(Carta(palo, valor)) def muestraMazo(self): for carta in self.cartas: print carta
    • 246 Listados Completos de Python def __str__(self): s = "" for i in range(len(self.cartas)): s = s + " "*i + str(self.cartas[i]) + "n" return s def mezclar(self): import random nCartas = len(self.cartas) for i in range(nCartas): j = random.randrange(i, nCartas) self.cartas[i], self.cartas[j] = self.cartas[j], self.cartas[i] def eliminaCarta(self, carta): if carta in self.cartas: self.cartas.remove(carta) return 1 else: return 0 def darCarta(self): return self.cartas.pop() def estaVacio(self): return (len(self.cartas) == 0) def repartir(self, manos, nCartas=999): nManos = len(manos) for i in range(nCartas): if self.estaVacio(): break # fin si se acaban las cartas carta = self.darCarta() # da la carta superior mano = manos[i % nManos] # a qui´n le toca? e mano.agregaCarta(carta) # agrega la carta a la manoclass Mano(Mazo): def __init__(self, nombre=""): self.cartas = [] self.nombre = nombre def agregaCarta(self,carta) : self.cartas.append(carta)
    • C.3 Cartas, mazos y juegos 247 def __str__(self): s = "La mano de " + self.nombre if self.estaVacio(): s = s + " est´ vac´an" a ı else: s = s + " contienen" return s + Mazo.__str__(self)class JuegoDeCartas: def __init__(self): self.mazo = Mazo() self.mazo.mezclar()class ManoDeLaMona(Mano): def eliminaCoincidencias(self): cant = 0 cartasOriginales = self.cartas[:] for carta in cartasOriginales: empareja = Carta(3 - carta.palo, carta.valor) if empareja in self.cartas: self.cartas.remove(carta) self.cartas.remove(empareja) print "Mano %s: %s con %s" % (self.nombre,carta,empareja) cant = cant + 1 return cantclass JuegoDeLaMona(JuegoDeCartas): def jugar(self, nombres): # quitamos la Reina de Tr´boles e self.mazo.eliminaCarta(Carta(0,12)) # construimos una mano para cada jugador self.manos = [] for nombre in nombres : self.manos.append(ManoDeLaMona(nombre)) # repartimos los naipes self.mazo.repartir(self.manos) print "----- Se han repartido las cartas." self.muestraManos()
    • 248 Listados Completos de Python # eliminamos las coincidencias iniciales emparejadas = self.eliminaTodasLasCoincidencias() print "----- Coincidencias eliminadas, el juego comienza." self.muestraManos() # se juega hasta que se han descartado las 50 cartas turno = 0 cantManos = len(self.manos) while emparejadas < 25: emparejadas = emparejadas + self.jugarUnTurno(turno) turno = (turno + 1) % cantManos print "----- El juego termin´." o self.muestraManos() def eliminaTodasLasCoincidencias(self): cant = 0 for mano in self.manos: cant = cant + mano.eliminaCoincidencias() return cant def jugarUnTurno(self, i): if self.manos[i].estaVacio(): return 0 vecino = self.encuentraVecino(i) cartaElegida = self.manos[vecino].darCarta() self.manos[i].agregaCarta(cartaElegida) print "Mano", self.manos[i].nombre, "eligi´", cartaElegida o cant = self.manos[i].eliminaCoincidencias() self.manos[i].mezclar() return cant def encuentraVecino(self, i): cantManos = len(self.manos) for proximo in range(1,cantManos): vecino = (i + proximo) % cantManos if not self.manos[vecino].estaVacio(): return vecino def muestraManos(self) : for mano in self.manos : print mano
    • C.4 Lists Enlazadas 249C.4. Lists Enlazadas def imprimeLista(nodo): while nodo: print nodo, nodo = nodo.siguiente print def imprimeAlReves(lista): if lista == None: return cabeza = lista cola = lista.siguiente imprimeAlReves(cola) print cabeza, def imprimeAlRevesBonito(lista) : print "[", if lista != None : cabeza = lista cola = lista.siguiente imprimeAlReves(cola) print cabeza, print "]", def eliminaSegundo(lista): if lista == None: return primero = lista segundo = lista.siguiente primero.siguiente = segundo.siguiente segundo.siguiente = None return segundoclass Nodo: def __init__(self, carga=None, siguiente=None): self.carga = carga self.siguiente = siguiente def __str__(self): return str(self.carga)
    • 250 Listados Completos de Python def imprimeAlReves(self): if self.siguiente != None: cola = self.siguiente cola.imprimeAlReves() print self.carga,class ListaEnlazada : def __init__(self) : self.longitud = 0 self.cabeza = None def imprimeAlReves(self): print "[", if self.cabeza != None: self.cabeza.imprimeAlReves() print "]", def agregaPrimero(self, carga): nodo = Nodo(carga) nodo.siguiente = self.cabeza self.cabeza = nodo self.longitud = self.longitud + 1C.5. Clase Pilaclass Pila : # implem. con listas de Python def __init__(self) : self.elementos = [] def push(self, elemento) : self.elementos.append(elemento) def pop(self) : return self.elementos.pop() def isEmpty(self) : return (self.elementos == []) def evalPostfijo(expr):
    • C.6 Colas y colas priorizadas 251 import re listaTokens = re.split("([^0-9])", expr) pila = Pila() for token in listaTokens: if token == ’’ or token == ’ ’: continue if token == ’+’: suma = pila.pop() + pila.pop() pila.push(suma) elif token == ’*’: producto = pila.pop() * pila.pop() pila.push(producto) else: pila.push(int(token)) return pila.pop()C.6. Colas y colas priorizadasclass Cola : def __init__(self) : self.longitud = 0 self.cabeza = None def vacia(self) : return (self.longitud == 0) def inserta(self, carga) : nodo = Nodo(carga) nodo.siguiente = None if self.cabeza == None : # Si la lista est´ vac´a nuestro nuevo nodo es el primero a ı self.cabeza = nodo else : # Encuentra el ´ltimo nodo de la lista u ultimo = self.cabeza while ultimo.siguiente : ultimo = ultimo.siguiente # A~ada el nuevo nodo n ultimo.siguiente = nodo self.longitud = self.longitud + 1
    • 252 Listados Completos de Python def quita(self) : carga = self.cabeza.carga self.cabeza = self.cabeza.next self.longitud = self.longitud - 1 return cargaclass ColaMejorada : def __init__(self) : self.longitud = 0 self.cabeza = None self.ultimo = None def vacia(self) : return (self.longitud == 0) def inserta(self, carga) : nodo = Nodo(carga) nodo.siguiente = None if self.longitud == 0 : # Si la lista est´ vac´a nuestro nuevo nodo es el primero a ı self.cabeza = self.ultimo = nodo else : # Encuentra el ultimo nodo de la lista ultimo = self.ultimo # A~ade nuestro nodo nuevo n ultimo.siguiente = nodo self.ultimo = nodo self.longitud = self.longitud + 1 def quita(self) : carga = self.cabeza.carga self.cabeza = self.cabeza.siguiente self.longitud = self.longitud - 1 if self.longitud == 0 : self.ultimo = None return cargaclass ColaPriorizada : def __init__(self) : self.elementos = [] def vacia(self) :
    • ´C.7 Arboles 253 return self.elementos == [] def inserta(self, elemento) : self.elementos.append(elemento) def quita(self) : maxi = 0 for i in range(1,len(self.elementos)) : if self.elementos[i] > self.elementos[maxi] : maxi = i elemento = self.elementos[maxi] self.elementos[maxi:maxi+1] = [] return elementoclass Golfista : def __init__(self, nombre, puntos) : self.nombre = nombre self.puntos = puntos def __str__(self) : return "%-15s: %d" % (self.nombre, self.puntos) def __cmp__(self, otro) : if self.puntos < otro.puntos : return 1 # menos es m´s a if self.puntos > otro.puntos : return -1 return 0C.7. ´ Arbolesclass Arbol : def __init__(self, carga, izquierda=None, derecha=None) : self.carga = carga self.izquierda = izquierda self.derecha = derecha def __str__(self) : return str(self.carga) def tomaCarga(self): return self.carga def tomaIzquierda(self): return self.izquierda
    • 254 Listados Completos de Python def tomaDerecha(self): return self.derecha def ajustaCarga(self, carga): self.carga = carga def ajustaIzquierda (self, izquierda): self.left = izquierda def ajustaDerecha(self, derecha): self.derecha = derechadef total(arbol) : if arbol == None : return 0 return total(arbol.izquierda) + total(arbol.derecha) + arbol.cargadef imprimeArbol(arbol): if arbol == None: return print arbol.carga, imprimeArbol(arbol.izquierda) imprimeArbol(arbol.derecha)def imprimeArbolPosfijo(arbol): if arbol == None: return imprimeArbolPosfijo(arbol.izquierda) imprimeArbolPosfijo(arbol.derecha) print arbol.carga,def imprimeArbolInfijo(arbol): if arbol == None: return imprimeArbolInfijo(arbol.izquierda) print arbol.carga, imprimeArbolInfijo(arbol.derecha)def imprimeArbolSangrado(arbol, nivel=0): if arbol == None: return imprimeArbolSangrado(arbol.derecha, nivel+1) print ’ ’*nivel + str(arbol.carga) imprimeArbolSangrado(arbol.izquierda, nivel+1)C.8. ´ Arboles de expresi´n odef tomaToken(listaToken, esperado): if listaToken[0] == esperado: listaToken[0:1] = [] # quita el token return 1 else:
    • C.9 Adivina el animal 255 return 0def obtieneProducto(listaToken) : a = obtieneNumero(listaToken) if tomaToken(listaToken, ’*’) : b = obtieneProducto(listaToken) return Arbol(’*’, a, b) else : return adef obtieneSuma(listaToken) : a = obtieneProducto(listaToken) if tomaToken(listaToken, ’+’) : b = obtieneSuma(listaToken) return Arbol(’+’, a, b) else : return adef obtieneNumero(listaToken): if tomaToken(listaToken, ’(’) : x = obtieneSuma(listaToken) # obtiene subexpresi´n o tomaToken(listaToken, ’)’) # se come el cierre de par´ntesis e return x else : x = listaToken[0] if type(x) != type(0) : return None listaToken[0:1] = [] # quita el token return Arbol(x, None, None) # devuelve una hoja sin el n´mero uC.9. Adivina el animaldef animal(): # empezar con un nodo suelto raiz = Arbol("p´jaro") a # bucle hasta que el usuario salga while 1: print if not si("Est´s pensando en un animal? "): break a # recorrer el ´rbol a
    • 256 Listados Completos de Python arbol = raiz while arbol.tomaIzquierda() != None: indicador = arbol.tomaCarga() + "? " if si(indicador): arbol = arbol.tomaDerecha() else: arbol = arbol.tomaIzquierda() # intentar adivinar adivina = arbol.tomaCarga() indicador = "Es un " + adivina + "? " if si(indicador): print "^<Soy el m´s grande!" A a continue # obtener informaci´n nueva o indicador = "C´mo se llama el animal? " o animal = raw_input(indicador) indicador = "Qu´ pregunta distinguir´a a un %s de un %s? " e ı pregunta = raw_input(indicador % (animal,adivina)) # a~adir informaci´n nueva al ´rbol n o a arbol.ponCarga(pregunta) indicador = "Si el animal fuera un %s, cu´l ser´a la respuesta? " a ı if si(indicador % animal): arbol.ponIzquierda(Arbol(adivina)) arbol.ponDerecha(Arbol(animal)) else: arbol.ponIzquierda(Arbol(animal)) arbol.ponDerecha(Arbol(adivina))def si(preg): from string import lower resp = lower(raw_input(preg)) return (resp[0:1] == ’s’)C.10. Fraction classclass Fraccion: def __init__(self, numerador, denominador=1): m = mcd (numerador, denominador)
    • C.10 Fraction class 257 self.numerador = numerador / m self.denominador = denominador / m def __mul__(self, otro): if type(otro) == type(5): otro = Fraccion(otro) return Fraccion(self.numerador * otro.numerador, self.denominador * otro.denominador) __rmul__ = __mul__ def __add__(self, otro): if type(otro) == type(5): otro = Fraccion(otro) return Fraccion(self.numerador * otro.denominador + self.denominador * otro.numerador, self.denominador * otro.denominador) __radd__ = __add__ def __cmp__(self, otro): if type(otro) == type(5): otro = Fraccion(otro) dif = (self.numerador * otro.denominador - otro.numerador * self.denominador) return dif def __repr__(self): return self.__str__() def __str__(self): return "%d/%d" % (self.numerador, self.denominador)def mcd(m,n): "devuelve el m´ximo com´n denominador de dos enteros" a u if m % n == 0: return n else: return mcd(n,m%n)
    • Ap´ndice D eLecturas recomendadasY ahora, ¿hacia d´nde ir desde aqu´ Hay muchas direcciones en las que seguir, o ı?ampliando sus conocimientos de Python spec´ ıficamente y de inform´tica en ageneral.Los ejemplos en este libro han sido deliberadamente simples, por lo que puedenno haber mostrado las capacidades m´s excitantes de Python. A continuaci´n a oexponemos una muestra de las extensiones de Python y sugerencias sobre sususos. La programaci´n de GUIs (interfaces gr´ficas de usuario, graphic user o a interface en ingl´s) permite que su programa utilice un entorno de ventanas e para interactuar con el usuario y mostrar gr´ficos. a El primer paquete que ha tenido Python para esto es Tkinter, basado en los lenguajes interpretados Tcl y Tk de Jon Ousterhout. Tkinter est´ incluido a en la distribuci´n de Python. o Otra plataforma popular es wxPython, que es esencialmente un encha- pado sobre wxWindows, un paquete de C++ que implementa ventanas utilizando la interfaces nativas las plataformas Windows y Unix (incluido Linux). Las ventanas y los controles con wxPython tienen una apariencia m´s nativa que Tkinter y son un poco m´s sencillos de programar. a a Cualquier tipo de programaci´n de GUIs le llevar´ a programaci´n basada o a o en eventos, donde es el usuario y no el programador quien determina el flujo de la ejecuci´n. Este estilo de programaci´n requiere de algo de o o tiempo para acostumbrarse, y a veces le forzar´ a replantearse toda la a estructura del programa.
    • 260 Lecturas recomendadas La programaci´n web integra Python en la Internet. Por ejemplo, puede o construir programas de cliente web que abran y lean una p´gina remota a (casi) tan f´cilmente como si fuera un fichero en disco. Tambi´n hay m´du- a e o los de Python que le permiten acceder a ficheros remotamente v´ ftp, y ıa m´dulos que le permiten enviar y recibir correos electr´nicos. Python tam- o o bi´n es ampliamente utilizado en el lado del servidor de la programaci´n e o web para manejar los datos de entrada de los formularios. Las bases de datos son un poco como super ficheros en donde los datos est´n almacenados en esquemas predefinidos, y las relaciones entre los a datos le permiten acceder a ellos de varias maneras. Python tiene varios m´dulos para permitir a los usuarios conectarse a varios motores de bases o de datos, tanto Open Source como comerciales. La programaci´n multi-procesos (multi-hilos) le permite ejecutar varios o procesos (hilos) de ejecuci´n dentro de un unico programa. Si ha tenido la o ´ experiencia de usar un navegador web para desplazarse por una p´gina web a mientras el navegador contin´a cargando el resto de la misma, entonces u tiene una idea de lo que los hilos pueden hacer. Cuando la velocidad es m´s importante se pueden escribir extensiones a para Python en un lenguaje compilado como C o C++. Tales extensio- nes forman la base de la mayor´ de m´dulos en la librer´ de Python. ıa o ıa El mecanismo de enlazar funciones y datos es un poco complejo. SWIG (Simplified Wrapper and Interface Generator) es una herramienta para hacer este proceso mucho m´s sencillo. aD.1. Libros y sitios web sobre PythonAqu´ tiene las recomendaciones de los autores sobre recursos para Python en la ıweb: La p´gina de inicio de Python en www.python.org es el lugar para empezar a su b´squeda de material sobre Python. Encontrar´ ayuda, documentaci´n, u a o enlaces a otros libros y listas de correo de SIGs (Special Interest Group) a las que se puede unir. El proyecto Open Book Project www.ibiblio.com/obp contiene no s´lo o este libro en l´ ınea sino tambi´n otros libros similares para Java y C++ e de Allen Downey. Adem´s est´ Lessons in Electric Circuits de Tony R. a a Kuphaldt, Getting down with ..., un conjunto de tutoriales de varios temas sobre inform´tica, escritos y editados por estudiantes de institulo, Python a for Fun, un conjuto de estudios de casos en Python de Chris Meyers, y The Linux Cookbook de Michael Stultz, con 300 p´ginas de trucos y t´cnicas. a e
    • D.2 Libros recomendados sobre inform´tica en general a 261 Finalmente si acude a Google y busca con la cadena “python -snake - monty” obtendr´ cerca de 750.000 resultados. aY aqu´ algunos libros que contienen m´s material sobre el lenguaje Python: ı a Core Python Programming de Wesley Chun es un libro largo, m´s de 750 a p´ginas. La primera parte del libro cubre las caracter´ a ısticas b´sicas del a lenguaje Python. La segunda parte proporciona una introducci´n paso o a paso a temas m´s avanzados incluyendo muchos de los mencionados a anteriormente. Python Essential Reference de David M. Beazley es un libro peque˜o, n pero contiene informaci´n sobre el lenguaje en s´ mismo y los m´dulos de o ı o la librer´ est´ndar. Tambi´n est´ muy bien indexado. ıa a e a Python Pocket Reference de Mark Lutz realmente cabe en el bolsillo. Aun- que no es tan extensivo como Python Essential Reference es una referencia util para los m´dulos y funciones m´s comunmente usadas. Mark Lutz ´ o a tambi´n es autor de Programming Python, uno de los primeros (y m´s e a largos) libros de Python y no est´ dirigido al programador principiante. a Su siguiente libro Learning Python es m´s peque˜o y m´s accesible. a n a Python Programming on Win32 de Mark Hammond y Andy Robinson es un libro que “debe tener” cualquiera que que utilice seriamente Pyt- hon para desarrollar aplicaciones para Windows. Entre otras cosas cubre la integraci´n de Python y COM, construye una peque˜a aplicaci´n con o n o wxPython, e incluso utiliza Python para escribir scripts para aplicaciones tales como Word y Excel.D.2. Libros recomendados sobre inform´tica en a generalLas siguientes sugerencias sobre lecturas adicionales incluyen muchos de loslibros favoritos de los autores. Estos tratan sobre buenas pr´cticas de progra- amaci´n e inform´tica en general. o a The Practice of Programming de Kernighan y Pike cubre no s´lo el dise˜o o n y dodificaci´n de algoritmos y estructuras de datos, sino tambi´n depura- o e ci´n, testeo y mejora de rendimiento de los programas. Los ejemplos est´n o a principalmente en C++ y Java, sin nada de Python.
    • 262 Lecturas recomendadas The Elements of Java Style editado por Al Vermeulen es otro libro peque˜o n que discute algunos de los puntos m´s sutiles de la buena programaci´n, a o tales como el buen uso de las convenciones de nombres, comentarios e indentaci´n (un poco irrelevante en Python). El libro tambi´n cubre la o e programaci´n por contrato, usando aserciones para encontrar los errores o probando precondiciones y postcondiciones, y programaci´n correcta con o hilos y su sincronizaci´n. o Programming Pearls de Jon Bentley es un libro cl´sico. Consiste en es- a tudios de caso que aparecieron originalmente en la columna del autor en Communications of the ACM. Los estudios tratan sobre toma y daca en programaci´n y por qu´ suele ser mala idea desarrollar con la primera idea o e de un programa. El libro es un poco m´s antiguo que los anteriores (1986), a por lo que los ejemplos est´n en lenguajes m´s antiguos. Hay muchos pro- a a blemas para resolver, algunos con soluciones y otros con pistas. Este libro fue muy popular y le sigui´ un segundo volumen. o The New Turing Omnibus de A.K Dewdney proporciona una introduc- ci´n amigable a 66 temas de inform´tica desde computaci´n en parelelo o a o hasta virus inform´ticos, desde TACs (tomograf´ computerizadas) hasta a ıas algoritmos gen´ticos. Todos los temas son cortos y entretenidos. Un libro e anterior de Dewdney Aventuras Inform´ticas es una colecci´n de su co- a o lumna Juegos de ordenador en Invertigaci´n y Ciencia. Ambos libros son o ricas fuentes de ideas para proyectos. Tortugas, Termitas y Atascos de Tr´fico de Mitchel Resnick trata sobre a el poder de la descentralizaci´n y de como pueden obtenerse comporta- o mientos complejos a partir de las actividades simples de una multitud de agentes coordinados. Introduce el lenguaje StarLogo, que permite al usua- rio escribir programas para agentes. La ejecuci´n del programa demuestra o comportamientos complejos agregados, que suelen ser intuitivos. La ma- yor´ de los programas en el libro fueron desarrollados por estudiantes ıa de colegio e instituto. Programas similares pueden escribirse en Python usando gr´ficos e hilos. a G¨del, Escher, Bach de Douglas Hofstadter. Simplemente, si encuentra o magia en la recursi´n tambi´n la encontrar´ en este libro superventas. o e a Uno de los temas de Hofstadter concierne a los “lazos extra˜os” donde los n patrones se desenvuelven y ascienden hasta que se encuentran a s´ mismos ı de nuevo. Es una disputa de Hofstadter que tales “lazos extra˜os” son n una parte esencial de lo que separa lo animado de lo no animado. El ´ demuestra tales patrones en la m´sica de Bach, las ilustraciones de Escher u y el teorema de incompletitud de G¨del. o
    • Ap´ndice E eGNU Free DocumentationLicenseVersion 1.1, March 2000Copyright c 2000 Free Software Foundation, Inc.59 Temple Place, Suite 330, Boston, MA 02111-1307 USAEveryone is permitted to copy and distribute verbatim copies of this licensedocument, but changing it is not allowed.PreambleThe purpose of this License is to make a manual, textbook, or other written do-cument “free” in the sense of freedom: to assure everyone the effective freedomto copy and redistribute it, with or without modifying it, either commerciallyor noncommercially. Secondarily, this License preserves for the author and pu-blisher a way to get credit for their work, while not being considered responsiblefor modifications made by others.This License is a kind of “copyleft,” which means that derivative works of thedocument must themselves be free in the same sense. It complements the GNUGeneral Public License, which is a copyleft license designed for free software.We have designed this License in order to use it for manuals for free software,because free software needs free documentation: a free program should comewith manuals providing the same freedoms that the software does. But this
    • 264 GNU Free Documentation LicenseLicense is not limited to software manuals; it can be used for any textual work,regardless of subject matter or whether it is published as a printed book. Werecommend this License principally for works whose purpose is instruction orreference.E.1. Applicability and DefinitionsThis License applies to any manual or other work that contains a notice placedby the copyright holder saying it can be distributed under the terms of this Li-cense. The “Document,” below, refers to any such manual or work. Any memberof the public is a licensee, and is addressed as “you.”A “Modified Version” of the Document means any work containing the Docu-ment or a portion of it, either copied verbatim, or with modifications and/ortranslated into another language.A “Secondary Section” is a named appendix or a front-matter section of theDocument that deals exclusively with the relationship of the publishers or aut-hors of the Document to the Document’s overall subject (or to related matters)and contains nothing that could fall directly within that overall subject. (Forexample, if the Document is in part a textbook of mathematics, a SecondarySection may not explain any mathematics.) The relationship could be a matterof historical connection with the subject or with related matters, or of legal,commercial, philosophical, ethical, or political position regarding them.The “Invariant Sections” are certain Secondary Sections whose titles are de-signated, as being those of Invariant Sections, in the notice that says that theDocument is released under this License.The “Cover Texts” are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document isreleased under this License.A “Transparent” copy of the Document means a machine-readable copy, repre-sented in a format whose specification is available to the general public, whosecontents can be viewed and edited directly and straightforwardly with generictext editors or (for images composed of pixels) generic paint programs or (fordrawings) some widely available drawing editor, and that is suitable for input totext formatters or for automatic translation to a variety of formats suitable forinput to text formatters. A copy made in an otherwise Transparent file formatwhose markup has been designed to thwart or discourage subsequent modifica-tion by readers is not Transparent. A copy that is not “Transparent” is called“Opaque.”
    • E.2 Verbatim Copying 265Examples of suitable formats for Transparent copies include plain ASCII wit-hout markup, Texinfo input format, L TEX input format, SGML or XML using Aa publicly available DTD, and standard-conforming simple HTML designed forhuman modification. Opaque formats include PostScript, PDF, proprietary for-mats that can be read and edited only by proprietary word processors, SGMLor XML for which the DTD and/or processing tools are not generally availa-ble, and the machine-generated HTML produced by some word processors foroutput purposes only.The “Title Page” means, for a printed book, the title page itself, plus suchfollowing pages as are needed to hold, legibly, the material this License requiresto appear in the title page. For works in formats which do not have any titlepage as such, “Title Page” means the text near the most prominent appearanceof the work’s title, preceding the beginning of the body of the text.E.2. Verbatim CopyingYou may copy and distribute the Document in any medium, either commerciallyor noncommercially, provided that this License, the copyright notices, and thelicense notice saying this License applies to the Document are reproduced inall copies, and that you add no other conditions whatsoever to those of thisLicense. You may not use technical measures to obstruct or control the readingor further copying of the copies you make or distribute. However, you may acceptcompensation in exchange for copies. If you distribute a large enough numberof copies you must also follow the conditions in Section 3.You may also lend copies, under the same conditions stated above, and you maypublicly display copies.E.3. Copying in QuantityIf you publish printed copies of the Document numbering more than 100, andthe Document’s license notice requires Cover Texts, you must enclose the copiesin covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Textson the front cover, and Back-Cover Texts on the back cover. Both covers mustalso clearly and legibly identify you as the publisher of these copies. The frontcover must present the full title with all words of the title equally prominentand visible. You may add other material on the covers in addition. Copyingwith changes limited to the covers, as long as they preserve the title of theDocument and satisfy these conditions, can be treated as verbatim copying inother respects.
    • 266 GNU Free Documentation LicenseIf the required texts for either cover are too voluminous to fit legibly, you shouldput the first ones listed (as many as fit reasonably) on the actual cover, andcontinue the rest onto adjacent pages.If you publish or distribute Opaque copies of the Document numbering morethan 100, you must either include a machine-readable Transparent copy alongwith each Opaque copy, or state in or with each Opaque copy a publicly acces-sible computer-network location containing a complete Transparent copy of theDocument, free of added material, which the general network-using public hasaccess to download anonymously at no charge using public-standard networkprotocols. If you use the latter option, you must take reasonably prudent steps,when you begin distribution of Opaque copies in quantity, to ensure that thisTransparent copy will remain thus accessible at the stated location until at leastone year after the last time you distribute an Opaque copy (directly or throughyour agents or retailers) of that edition to the public.It is requested, but not required, that you contact the authors of the Documentwell before redistributing any large number of copies, to give them a chance toprovide you with an updated version of the Document.E.4. ModificationsYou may copy and distribute a Modified Version of the Document under theconditions of Sections 2 and 3 above, provided that you release the ModifiedVersion under precisely this License, with the Modified Version filling the roleof the Document, thus licensing distribution and modification of the ModifiedVersion to whoever possesses a copy of it. In addition, you must do these thingsin the Modified Version: Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission. List on the Title Page, as authors, one or more persons or entities respon- sible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its prin- cipal authors, if it has less than five). State on the Title page the name of the publisher of the Modified Version, as the publisher. Preserve all the copyright notices of the Document.
    • E.4 Modifications 267 Add an appropriate copyright notice for your modifications adjacent to the other copyright notices. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below. Preserve in that license notice the full lists of Invariant Sections and re- quired Cover Texts given in the Document’s license notice. Include an unaltered copy of this License. Preserve the section entitled “History,” and its title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section entitled “History” in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the “History” section. You may omit a network location for a work that was published at least four years before the Do- cument itself, or if the original publisher of the version it refers to gives permission. In any section entitled “Acknowledgements” or “Dedications,” preserve the section’s title, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles. Delete any section entitled “Endorsements.” Such a section may not be included in the Modified Version. Do not retitle any existing section as “Endorsements” or to conflict in title with any Invariant Section.If the Modified Version includes new front-matter sections or appendices thatqualify as Secondary Sections and contain no material copied from the Docu-ment, you may at your option designate some or all of these sections as invariant.
    • 268 GNU Free Documentation LicenseTo do this, add their titles to the list of Invariant Sections in the Modified Ver-sion’s license notice. These titles must be distinct from any other section titles.You may add a section entitled “Endorsements,” provided it contains nothingbut endorsements of your Modified Version by various parties—for example,statements of peer review or that the text has been approved by an organizationas the authoritative definition of a standard.You may add a passage of up to five words as a Front-Cover Text, and a passageof up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts inthe Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity.If the Document already includes a cover text for the same cover, previouslyadded by you or by arrangement made by the same entity you are acting onbehalf of, you may not add another; but you may replace the old one, on explicitpermission from the previous publisher that added the old one.The author(s) and publisher(s) of the Document do not by this License givepermission to use their names for publicity for or to assert or imply endorsementof any Modified Version.E.5. Combining DocumentsYou may combine the Document with other documents released under this Li-cense, under the terms defined in Section 4 above for modified versions, providedthat you include in the combination all of the Invariant Sections of all of theoriginal documents, unmodified, and list them all as Invariant Sections of yourcombined work in its license notice.The combined work need only contain one copy of this License, and multipleidentical Invariant Sections may be replaced with a single copy. If there aremultiple Invariant Sections with the same name but different contents, makethe title of each such section unique by adding at the end of it, in parentheses,the name of the original author or publisher of that section if known, or elsea unique number. Make the same adjustment to the section titles in the list ofInvariant Sections in the license notice of the combined work.In the combination, you must combine any sections entitled “History” in thevarious original documents, forming one section entitled “History”; likewise com-bine any sections entitled “Acknowledgements,” and any sections entitled “De-dications.” You must delete all sections entitled “Endorsements.”
    • E.6 Collections of Documents 269E.6. Collections of DocumentsYou may make a collection consisting of the Document and other documentsreleased under this License, and replace the individual copies of this Licensein the various documents with a single copy that is included in the collection,provided that you follow the rules of this License for verbatim copying of eachof the documents in all other respects.You may extract a single document from such a collection, and distribute itindividually under this License, provided you insert a copy of this License intothe extracted document, and follow this License in all other respects regardingverbatim copying of that document.E.7. Aggregation with Independent WorksA compilation of the Document or its derivatives with other separate and in-dependent documents or works, in or on a volume of a storage or distributionmedium, does not as a whole count as a Modified Version of the Document,provided no compilation copyright is claimed for the compilation. Such a com-pilation is called an “aggregate,” and this License does not apply to the otherself-contained works thus compiled with the Document, on account of their beingthus compiled, if they are not themselves derivative works of the Document.If the Cover Text requirement of Section 3 is applicable to these copies of theDocument, then if the Document is less than one quarter of the entire aggregate,the Document’s Cover Texts may be placed on covers that surround only theDocument within the aggregate. Otherwise they must appear on covers aroundthe whole aggregate.E.8. TranslationTranslation is considered a kind of modification, so you may distribute transla-tions of the Document under the terms of Section 4. Replacing Invariant Sectionswith translations requires special permission from their copyright holders, butyou may include translations of some or all Invariant Sections in addition tothe original versions of these Invariant Sections. You may include a translationof this License provided that you also include the original English version ofthis License. In case of a disagreement between the translation and the originalEnglish version of this License, the original English version will prevail.
    • 270 GNU Free Documentation LicenseE.9. TerminationYou may not copy, modify, sublicense, or distribute the Document except asexpressly provided for under this License. Any other attempt to copy, modify,sublicense, or distribute the Document is void, and will automatically terminateyour rights under this License. However, parties who have received copies, orrights, from you under this License will not have their licenses terminated solong as such parties remain in full compliance.E.10. Future Revisions of This LicenseThe Free Software Foundation may publish new, revised versions of the GNUFree Documentation License from time to time. Such new versions will be similarin spirit to the present version, but may differ in detail to address new problemsor concerns. See http:///www.gnu.org/copyleft/.Each version of the License is given a distinguishing version number. If the Do-cument specifies that a particular numbered version of this License .or any laterversion.applies to it, you have the option of following the terms and conditionseither of that specified version or of any later version that has been published(not as a draft) by the Free Software Foundation. If the Document does not spe-cify a version number of this License, you may choose any version ever published(not as a draft) by the Free Software Foundation.E.11. Addendum: How to Use This License for Your DocumentsTo use this License in a document you have written, include a copy of the Licensein the document and put the following copyright and license notices just afterthe title page: Copyright c YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version pu- blished by the Free Software Foundation; with the Invariant Sec- tions being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. A copy of the li- cense is included in the section entitled “GNU Free Documentation License.”
    • E.11 Addendum: How to Use This License for Your Documents 271If you have no Invariant Sections, write “with no Invariant Sections” insteadof saying which ones are invariant. If you have no Front-Cover Texts, write“no Front-Cover Texts” instead of “Front-Cover Texts being LIST”; likewise forBack-Cover Texts.If your document contains nontrivial examples of program code, we recommendreleasing these examples in parallel under your choice of free software license,such as the GNU General Public License, to permit their use in free software.
    • ´Indice alfab´tico e´rbol, 211a asignaci´n, 12, 20, 61 o expresi´n, 213, 216 o tupla, 168 recorrido, 213, 214 tuplas, 100 vac´ 212 ıo, asignaci´n de alias, 112 o´rbol binario, 211, 224a asignaci´n de tuplas, 100, 107, 168 o´rbol de expresi´n, 213, 216a o asignaci´n m´ltiple, 61, 73 o u´ındice, 76, 84, 97, 109, 229 asignmaci´no negativo, 76 tupla, 107Make Way for Ducklings, 77 atributo, 139Referencia de la Biblioteca de Pyt- clase, 163, 170 hon, 83 atributo de clase, 163, 170 atributos, 132, 33 AttributeError, 230abeced´rico, 77 aacceso, 86 barajar, 167acto de fe, 55, 186 base de conocimiento, 221acumulador, 167, 170, 178 bifurcaci´n condicional, 37 oaleatorio, 167 bloque, 37, 45algoritmo, 10, 146, 148 booleanaalias, 92, 97 expresi´n, 36 oambig¨edad, 7, 134 u booleanas teorema fundamental, 188 funciones, 52an´lisis sint´ctico, 10 a a borradoanalizar, 199, 216 lista, 91analizar sint´cticamente, 7, 201 a borrado en una lista, 91andamiaje, 48, 59 bucle, 63, 73anidamiento, 45 anidado, 165archivo, 128 condici´n, 228 o texto, 121 cuerpo, 63, 73archivo de texto, 121, 128 en una lista, 187archivos, 119 for, 76argumento, 21, 33 infinito, 63, 73, 228argumentos, 28 recorrido, 76
    • 274 ´ Indice alfab´tico e variable de, 73 clonar, 97 while, 62 codificar, 162, 170bucle for, 76, 88 coerci´n, 33 obucle infinito, 227, 228 tipo, 115buffer circular, 210 coerci´n de tipos, 22 o coercion, 23c´digo de objeto, 10 o coincidencia de esquemas, 107c´digo ejecutable, 10 o cola, 203, 210c´digo fuente, 10 o implementaci´n con Lista, 203 oc´digo muerto, 48, 59 o implementaci´n enlazada, 204 ocadena, 11, 12 implementaci´n mejorada, 205 o inmutable, 79 cola enlazada, 204, 210 longitud, 76 cola mejorada, 205 porci´n, 78 o cola priorizada, 203, 210cadena de formato, 123, 128 TAD, 207cadena inmutable, 79 colecci´n, 185, 196 ocaja, 114 columna, 96caja de funci´n, 114 ocar´cter, 75 a coma flotante, 20, 131carga, 183, 192, 211 comentario, 20Carta, 161 comentarios, 19caso base, 43, 45 comparable, 164cifrar, 162 comparaci´n oclase, 131, 139 cadenas, 78 Carta, 161 fracci´n, 239 o Golfista, 209 comparaci´n de cadenas, 78 o JuegoDeLaMona, 177 compilador, 225 ListaEnlazada, 190 compilar, 2, 10 ManoDeLaMona, 176 composici´n, 18, 20, 24, 51, 161, 165 o Nodo, 183 compresi´n, 116 o padre, 172, 175 comprobaci´n de errores, 57 o Pila, 196 concatenaci´n, 77, 80 o Punto, 155 concatenar, 20clase abstracta, 210 concatenation, 18clase hija, 171, 182 condici´n, 63 oclase padre, 171, 172, 175, 182 condici´n, 45, 228 oclase Punto, 155 condici´n previa, 188, 192 oclasificaci´n o condiciones car´cter, 82 a encadenadas, 38clasificaci´n de caracteres, 82 o condiciones encadenadas, 38clave, 109, 117 constructor, 131, 139, 162cliente, 196, 201 contador, 80, 84clonado, 93, 112 conteo, 103
    • ´Indice alfab´tico e 275conversi´n de tipo, 33 o m´todos, 111 econversi´n de tipos, 22 o operaciones sobre, 110copia profunda, 139 directorio, 125, 128copia superficial, 139 dise˜o orientado a objetos, 171 ncopiado, 112, 137 divisi´n de enteros, 16, 20, 22 ocorresponder, 162 documentaci´n, 192 ocuelgue, 227 Doyle, Arthur Conan, 6cuerpo, 37, 45 bucle, 63 ejecuci´n ocursor, 73 flujo, 229 ejecuci´n condicional, 37 odecrementar, 84 elemento, 85, 97definici´n o eliminaci´n de naipes, 168 o circular, 53 encapsulaci´n, 136, 195, 200 o recursiva, 219 encapsulado, 67definici´n circular, 53 o encapsular, 73definici´n de funci´n, 25, 33 o o enchapado, 197definici´n recursiva, 219 o encolamiento priorizado, 203delimitador, 97, 125, 199, 201 encurtido, 125, 128denominador, 235 enlace, 192depuraci´n, 10, 225 o enterosdepuraci´n (debugging), 4 o divisi´n, 22 odesarrollo largos, 115 incremental, 148 enteros largos, 115 planificado, 148 envoltorio, 192desarrollo de progamas error encapsulado, 67 de sintaxis, 225desarrollo de programas, 73 en tiempo de compilaci´n, 225 o generalizaci´n, 67 o en tiempo de ejecuci´n, 43, 225 odesarrollo de prototipos, 145 sem´ntico, 225, 231 adesarrollo incremental, 49, 59, 148 sintaxis, 5desarrollo incremental de progra- tiempo de ejecuci´n, 5 o mas, 226 error (bug), 4desarrollo planificado, 148 error de sintaxis, 225desbordamiento, 115 error en tiempo de compilaci´n, 225 odeterminista, 107 error en tiempo de ejecuci´n, 5, 10, odiagrama de estado, 13, 20 43, 76, 79, 87, 100, 111,diagrama de pila, 33 113, 115, 120, 124, 225,diagramas de pila, 30, 42 229diccionario, 96, 109, 117, 124, 230 error sem´ntico, 5, 10, 101, 225, 231 a m´todos, 111 e error sint´ctico, 5, 10 a operaciones, 110 error(bug), 10diccionarios, 109 escalera de color, 174
    • 276 ´ Indice alfab´tico eescape funci´n de Fibonacci, 113 o secuencia de, 73 funci´n factorial, 54, 57 oespacio en blanco, 84 funci´n gamma, 57 oestilo funcional de programaci´n, o funci´n pura, 142, 148 o 144, 148 funcionesestructura anidada, 161 argumentos, 28estructura de datos composici´n, 24, 51 o gen´rica, 196, 197 e llamadas, 21 recursiva, 183, 192, 212 par´metros, 28 aestructura de datos recursiva, 212 recursivas, 42estructura gen´rica de datos, 196, e tuplas como valor de retorno, 197 101estructura recursiva de datos, 183, funciones booleanas, 52 192 funciones matem´ticas, 23 aEuclides, 238excepci´n, 5, 10, 126, 128, 225, 229 o generalizaci´n, 67 oexpresi´n, 16, 20, 198 o generalizaci´n, 136, 146 o booleana, 45 generalizar, 73 grande y peliaguda, 232 Golfista, 209expresi´n booleana, 36, 45 o gr´fico de llamadas, 114 aexpresi´n regular, 199 o guardi´n, 59 a gui´n, 10 oFibonacci function, 56 gui´n bajo, 14 oFIFO, 203, 210fila, 96 herencia, 171, 182float, 12 histograma, 106, 107, 116flujo de ejecuci´n, 27, 33, 229 o Holmes, Sherlock, 6formal lenguaje, 6 identidad, 134forzado de tipo de datos, 115 igualdad, 134fracci´n, 235 o igualdad profunda, 134, 139 comparaci´n, 239 o igualdad superficial, 134, 139 multiplicaci´n, 236 o implementaci´no suma, 237 Cola, 203frangoso, 53 imponer, 160funci´n, 71 o impresi´n ofunci´n join, 96 o mano de cartas, 174funci´n split, 96 o objeto mazo, 166funci´n, 25, 33, 141, 150 o imprimir booleana, 169 objeto, 133, 150 definici´n, 25 o incrementar, 84 factorial, 54 IndexError, 230funci´n booleana, 169 o indicador, 44, 45
    • ´Indice alfab´tico e 277infijo, 198, 201 lenguaje formal, 6, 10infinito lenguaje natural, 6, 10, 134 bucle, 63 lenguaje seguro, 5infio, 213 Linux, 6inmutable, 99 lista, 85, 97, 183instancia, 133, 136, 139 anidada, 95, 112 objeto, 132, 150, 164 bien construida, 191instancia de objeto, 164 bucle, 187instancia de un objeto, 132, 150 bucle for, 88instanciaci´n, 132 o como par´metro, 185 ainstanciar, 139 de objetos, 165instrucci´n, 4 o elemento, 86int, 12 enlazada, 183, 192Intel, 64 impresi´n, 185 ointercambio, 168 imprimir hacia atr´s, 186 ainterfaz, 196, 210 infinita, 187interpretar, 2, 10 longitud, 87invariante, 191, 192 modificar, 189invocar, 117 mutable, 90invocar m´todos, 111 e pertenencia, 88irracional, 240 porciones, 90iteraci´n, 61, 62, 73 o recorrer recursivamente, 186 recorrido, 87, 185juego lista anidada, 97, 112 animales, 221 lista enlazada, 183, 192juego de los animales, 221 lista infinita, 187KeyError, 230 ListaEnlazada, 190 listasl´gico o anidadas, 85, 95 operador, 36 clonado, 93lanzar una excepci´n, 126, 128 o como par´metros, 94 alenguaje, 134 operaciones con, 89 alto nivel, 2 listas anidadas, 95 bajo nivel, 2 literalidad, 7 completo, 53 llamada a funci´n, 33 o programaci´n, 1 o llamadas a funciones, 21lenguaje completo, 53 locallenguaje de alto nivel, 2, 10 variable, 69lenguaje de bajo nivel, 2, 10 localeslenguaje de programaci´n, 1 o variables, 29lenguaje de programaci´n orientado o logaritmo, 64 a objetos, 149, 160 longitud, 87
    • 278 ´ Indice alfab´tico elowercase, 82 modifcador, 148 modificador, 143m´ximo com´n divisor, 238, 241 a u modificar listas, 189m´todo, 111, 117, 141, 150, 160 e multiplicaci´n o ayudante, 190 fracci´n, 236 o envoltorio, 190 multiplicaci´n escalar, 156, 160 o inicializaci´n, 154, 165 o mutable, 79, 84, 99 invocaci´n, 111 o lista, 90 lista, 116, 166m´todo append, 166 e n´mero um´todo ayudante, 190, 192 e aleatorio, 101m´todo de inicializaci´n, 154, 160, e o n´mero aleatorio, 101 u 165 NameError, 229m´todo de lista, 116 e naturalm´todo envoltorio, 190 e lenguaje, 6m´todos de desarrollo e negaci´n, 240 o incremental, 49 negaci´n unitaria, 241 om´todos de lista, 166 e nivel, 211, 224m´todos sobre diccionarios, 111 e nodo, 183, 192, 211, 224m´dulo, 23, 33, 81 o Nodo clase, 183 copy, 137 nodo de un ´rbol, 211 a operador, 35 nodo hermano, 224 string, 83 nodo hijo, 211, 224m´dulo copy, 137 o nodo hoja, 211, 224m´dulo string, 81, 83 o nodo padre, 211, 224m´ltiple u nodo ra´ 211, 224 ız, asignaci´n, 73 o None, 48, 59manejar errores, 220 notaci´n de punto, 111, 151, 154 omanejar una excepci´n, 126, 128 o nueva l´ınea, 73manejo de errores, 220 numerador, 235marco, 42marco de funci´n, 42 o objeto, 91, 97, 131, 139matem´ticas a mudable, 136 funciones, 23 objeto invariante, 191matriz, 95 objeto mudable, 136 dispersa, 112 objetosmazo, 165 lista de, 165McCloskey, Robert, 77 obst´culo al rendiminto, 210 amensajes de error, 225 operacionesmismidad, 134 con listas, 89modelo operaciones con listas, 89 mental, 232 operaciones sobre cadenas, 17modelo mental, 232 operador, 16, 20
    • ´Indice alfab´tico e 279 binario, 213, 224 Pila, 196 condicional, 164 pila, 196 corchete, 75 pista, 113, 117 formato, 123, 128, 209, 230 plan de desarrollo, 73 in, 88, 169 poes´ 8 ıa, m´dulo, 173 o polim´rfica, 160 o sobrecarga, 156, 236 polimorfismo, 158operador binario, 213, 224 pop, 197operador condicional, 164 porci´n, 78, 84 ooperador corchete, 75 porciones, 90operador de formato, 123, 128, 209, portabilidad, 10 230 portable, 2operador in, 88, 169 postfijo, 198, 201, 213operador l´gico, 36 o precedencia, 20, 232operador m´dulo, 35, 45, 173 o prefijo, 214, 224operador matem´tico, 236 a printoperador unitario, 240 sentencia, 9, 10operadores prioridad, 209 para listas, 89 producto, 219operando, 16, 20 producto interior, 156, 160orden, 164 programa, 10orden completo, 164 desarrollo de, 73orden de evaluaci´n, 232 o programaci´n orientada a objetos, oorden de las operaciones, 17 149, 171orden infijo, 214, 215, 224 prosa, 8orden parcial, 164 proveedor, 196, 201orden postfijo, 214, 215, 224 pseudoaleatorio, 107orden prefijo, 214, 224 pseudoc´digo, 238 o push, 197palabra reservada, 13, 20palabras reservadas, 14 racional, 235palo, 161 rama, 38, 45papel random, 167 variable, 188 randrange, 167par clave-valor, 109, 117 recorrer, 84, 185, 186, 213par´metros, 28 a recorrido, 76, 80, 88, 176, 208, 214par´metro, 33, 94, 133 a lista, 87 lista, 94 recorrido de lista, 97pass recorrido eureka, 80 sentencia, 37 rect´ngulo, 135 apatr´n, 80 o recuento, 116patr´n computacional, 80 o recursi´n, 213, 214 oPentium, 64 infinita, 228
    • 280 ´ Indice alfab´tico erecursi´n infinita, 227, 228 o sentencia except, 126, 128recursividad, 40, 42, 45, 53, 55 sentencia pass, 37 caso base, 43 sentencia print, 9, 10, 230 infinita, 43, 57 sentencia return, 40, 233recursividad infinita, 43, 45, 57 sentencia try, 126redimiento, 205 sentenciasreducir, 238, 241 bloque, 37redundancia, 7 compuestas, 37referencia, 183 sentencias compuestas, 37 incrustada, 183, 192 bloque de sentencias, 37referencia empotrada, 211 cabecera, 37referencia incrustada, 183, 192 cuerpo, 37referencias sequencia, 85 alias, 92 singleton, 189, 190, 192reglas de precedencia, 17, 20 sintaxis, 5, 10, 226reparto de naipes, 173 sobrecarga, 160, 236repetici´n o operador, 209 lista, 89 sobrecarga de operadores, 156, 160,runtime error, 76 164, 209ruta, 125 soluci´n de problemas, 10 o subclase, 171, 175, 182secuencia, 97 subexpresi´n, 220 osecuencia aritm´tica, 66 e suma, 219secuencia de escape, 66, 73 fracci´n, 237 osecuencia geom´trica, 66 e sustituir, 164seguro lenguaje, 5 t´ctica de encolamiento, 203, 210 asem´ntica, 5, 10 a tablas, 64 error, 5 dos dimensiones, 66sentencia, 20 tabulador, 73 asignaci´n, 12, 61 o TAD, 195, 200, 201 break, 121, 128 Cola, 203 condicional, 45 cola, 203 continue, 122, 128 Cola Priorizada, 203, 207 except, 126 Pila, 196 print, 230 TAD Cola, 203 return, 40, 233 temporal try, 126 variable, 59 while, 62 temporalessentencia break, 121, 128 variables, 48sentencia compuesta, 45 teoremasentencia condicional, 45 fundamental de la ambig¨edad, usentencia continue, 122, 128 188
    • ´Indice alfab´tico e 281teorema fundamental de la am- uppercase, 82 big¨edad, 192 u uso de alias, 137tesis de Turing, 53tiempo constante, 205, 210 valor, 20, 91, 161tiempo lineal, 205, 210 valor de retorno, 21, 33, 47, 59, 136tipo, 11, 12, 20 valores cadena, 12 tuplas, 101 float, 12 valores de retorno int, 12 tuplas, 101tipo abstracto de datos |verTAD, value, 11 195 variable, 12, 20tipo compuesto de datos, 84 bucle, 173tipo de datos papeles, 188 compuesto, 75, 131 temporal, 59, 232 definido por el usuario, 131, 235 variable de bucle, 73, 173, 185 diccionario, 109 variable local, 33tipo de datos compuesto, 75, 131 variable temporal, 232tipo de datos definido por el usua- variables rio, 131 locales, 69tipo de funci´n o variables locales, 29 modifcador, 143 variables temporales, 48 pura, 142tipo inmutable, 107 whiletipo mutable, 107 sentencia, 62tipos whitespace, 82 coerci´n, 22 o comprobaci´n, 57 o conversi´n, 22 otipos de datos enteros largos, 115 inmutables, 99 tuplas, 99token, 199, 201, 216traceback, 31traza, 127traza inversa, 43, 229try, 128tupla, 99, 107tuplas, 101Turing, Alan, 53TypeError, 229unidad, 10