SlideShare una empresa de Scribd logo
Aprenda a Pensar Como un Programador

                           con Python
Todo Sobre El Lenguaje 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.
    n


Green Tea Press
1 Grove St.
P.O. Box 812901
Wellesley, MA 02482


Se permite copiar, distribuir, y/o modificar este documento bajo los t´rminos de
                                                                     e
la GNU Free Documentation License, Versi´n 1.1 o cualquier versi´n posterior
                                            o                      o
publicada por la Free Software Foundation; siendo las Secciones Invariantes
“Pr´logo”, “Prefacio”, y “Lista de Colaboradores”, sin texto de cubierta, y
    o
sin texto de contracubierta. Se incluye una copia de la licencia en el ap´ndice
                                                                         e
titulado “GNU Free Documentation License”.
La GNU Free Documentation License est´ disponible en www.gnu.org o escri-
                                         a
biendo 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                  o
fuente L TE
        A X tiene el efecto de generar una representaci´n independiente del
                                                         o
dispositivo 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           o
Texto de C´digo Abierto est´n disponibles en
           o                a
       http://www.thinkpython.com
La composici´n de este libro se realiz´ utilizando L TEX y LyX. Las ilustraciones
             o                        o            A

se hicieron con xfig. Todos ellos son programas gratuitos de c´digo abierto.
                                                              o


Historia de la impresi´n:
                      o

Abril 2002: Primera edici´n.
                         o

ISBN 0-9716775-0-6
Pr´logo
  o

Por David Beazley
Como 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                                                         o
extremadamente f´cil de usar que en los ultimos a˜os se ha hecho muy popu-
                   a                       ´        n
lar. Desarrollado hace diez a˜os por Guido van Rossum, su sintaxis simple y la
                              n
sensaci´n general se deriva en gran parte del ABC, un lenguaje desarrollado en
       o
los 1980s para la ense˜anza. Sin embargo, Python tambi´n se cre´ para resolver
                      n                                  e        o
problemas reales y presenta una variedad amplia de caracter´ ısticas de lenguajes
de programaci´n como C++, Java, Modula-3 y Scheme. Debido a esto, una de
               o
las caracter´
            ısticas notables de Python es su atractivo para los desarrolladores
profesionales de progamaci´n, cient´
                            o      ıficos, investigadores, artistas, y educadores.
A pesar del atractivo de Python para muchas comunidades diferentes, puede
que a´n se pregunte “¿por qu´ Python?” o “¿por qu´ ense˜ar programaci´n
      u                         e                     e     n              o
con Python?”No es tarea f´cil responder a estas preguntas, en especial cuando
                           a
la opini´n popular est´ del lado de alternativas m´s masoquistas como C++ y
        o             a                           a
Java. Sin embargo, pienso que la respuesta m´s directa es que la progrmaci´n
                                               a                           o
en Python es simplemente m´s divertida y m´s productiva.
                              a               a
Cuando imparto cursos de inform´tica, quiero cubrir conceptos importantes,
                                    a
hacer 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                  o
demasiada atenci´n a la abstracci´n matem´tica que hace que los estudiantes
                  o                o         a
se frustren con problemas farragosos relacionados con detalles nimios de la sin-
taxis, compilaci´n, y la aplicaci´n de reglas aparentemente arcanas. Aunque
                o                o
tal abstraci´n y formalismo son importantes para ingenieros profesionales de la
            o
programaci´n y estudiantes que planean continuar sus estudios de inform´tica,
            o                                                             a
decidirse por este enfoque en un curso introductorio s´lo tiene ´xito en hacer
                                                       o         e
aburrida la inform´tica. Cuando imparto un curso, no quiero tener un aula de
                    a
estudiantes sin inspiraci´n. Quisiera verlos intentando resolver problemas in-
                         o
teresantes, explorando ideas diferentes, probando enfoques no convencionales,
vi                                                                      Pr´logo
                                                                          o

rompiendo las reglas, y aprendiendo de sus errores. Al hacerlo, no quiero perder
la mitad del semestre tratando de sortear problemas con una sintaxis abstru-
sa, mensajes de error del compilador incomprensibles, o los varios cientos de
maneras que un programa puede generar un error de proteci´n general.
                                                             o
Una 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
                               a
lenguaje interpretado, los principiantes pueden tomar el lenguaje y empezar a
hacer 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                                                o
se pueden usar para hacer toda clase de tareas que abarcan desde programaci´n  o
para web a gr´ficos. Este enfoque pr´ctico es una buena manera de enganchar a
              a                      a
estudiantes y permite que completen proyectos significativos. Sin embargo, Pyt-
hon tambi´n puede servir como una base excelente para intruducir conceptos
           e
importantes de inform´tica. Puesto que Python soporta completamente proce-
                       a
dimientos y clases, los estudiantes pueden introducirse gradualmente en temas
como abstracci´n procedural, estructuras de datos, y programaci´n orientada
                a                                                    o
objetos, que son aplicables a cursos posteriores en Java o C++. Python incluso
toma prestada cierta cantidad de caracter´  ısticas de lenguajes de programaci´n
                                                                               o
funcionales y puede usarse para intruducir conceptos que pudieran ser cubiertos
en 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   2

que puede “avanzar r´pido con mejores resultados”. Aunque estos comentarios
                      a
se refieren a sus cursos introductorios, a veces uso Python por estas mismas
razones en cursos de inform´tica avanzada en la Universidad de Chicago. En
                              a
estos cursos me enfrento constantemente con la desalentadora tarea de cubrir
un mont´n de material dif´ en un agotador trimestre de nueve semanas. Aun-
         o                 ıcil
que es ciertamente posible para m´ infligir mucho dolor y sufrimiento usando un
                                  ı
lenguaje como C++, he visto a menudo que ese estilo es ineficaz, especialmente
cuando el curso se trata de un asunto sin relaci´n apenas con la “programaci´n”.
                                                o                           o
Encuentro que usar Python me permite dedicarme m´s al asunto en cuesti´n
                                                        a                     o
mientras permito a los estudiantes completar proyectos utiles.
                                                          ´
Aunque Python es todav´ un lenguaje joven y en desarollo, creo que tiene
                          ıa
un futuro brillante en la educaci´n. Este libro es un paso importante en esa
                                 o
direcci´n.
       o


David Beazley
Universidad de Chicago
Autor de Python Essential Reference
Prefacio

Por Jeff Elkner
Este libro debe su existencia a la colaboraci´n hecha posible por la Internet y
                                             o
al movimiento de software libre. Sus tres autores, un profesor universitario, un
profesor de instituto y un programador profesional, todav´ tienen que conocerse
                                                         ıa
cara 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 esfuerzo
para ayudar a mejorar este libro.
Creemos que este libro es un testamento a los beneficios y futuras posibilidades
de este tipo de colaboraci´n, cuyo marco han establecido Richard Stallman y la
                          o
Free Software Foundation.



C´mo y por qu´ vine a usar Python
 o           e
En 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                                 o
institutos en todo el pa´ la decisi´n de cambiar de lenguaje tuvo un impacto
                        ıs,          o
directo sobre el curriculum de inform´tica en el Insituto de Yorktown en Ar-
                                       a
lington, Virgina, donde doy clase. Hasta ese momento, el lenguaje de ense˜anza
                                                                         n
era Pascal tanto en nuestro curso de primer a˜o como en el AP. Al seguir con
                                               n
la pr´ctica anterior de dar a los estudiantes dos a˜os de exposici´n al mismo
      a                                              n            o
lenguaje, tomamos la decisi´n de cambiar a C++ en el aula de primer a˜o del
                            o                                            n
curso 1997-98 de modo que estar´   ıamos en sinton´ con el cambio del Claustro
                                                  ıa
Escolar para el curso AP del a˜o siguiente.
                               n
Dos a˜os m´s tarde, me convenc´ de que C++ era una mala elecci´n para iniciar
      n    a                    ı                             o
a los estudiantes en la inform´tica. Aunque es un lenguaje de programaci´n
                               a                                          o
muy poderoso, tambi´n es extremadamente dif´ de aprender y ense˜ar. Me
                     e                          ıcil                n
encontr´ luchando constantemente con la dif´ sintaxis de C++ y sus m´ltiples
        e                                  ıcil                      u
formas de hacer las cosas, y como consecuencia perd´ muchos estudiantes sin
                                                     ıa
viii                                                                   Prefacio

necesidad. Convencido de que deb´ de haber una elecci´n mejor para el lenguaje
                                ıa                   o
de nuestro curso de primer a˜o, me puse a buscar una alternativa para C++.
                            n
Necesitaba un lenguaje que funcionase tanto en las m´quinas de nuestro labo-
                                                       a
ratorio de Linux como en las plataformas Windows y Macintosh que la mayor´    ıa
de los estudiantes ten´ en casa. Quer´ que fuera de c´digo abierto, para que
                       ıan              ıa               o
los estudiantes pudieran usarlo en casa sin importar su nivel econ´mico. Quer´
                                                                  o           ıa
un lenguaje utilizado por programadores profesionales, y que tuviera una co-
munidad activa de desarrolladores a su alrededor. Ten´ que soportar tanto la
                                                        ıa
programaci´n procedural como la orientada a objetos. Y lo m´s importante,
            o                                                    a
ten´ que ser f´cil de aprender y de ense˜ar. Cuando investigu´ las opciones con
    ıa        a                          n                     e
estos obejetivos en mente, Python destac´ como el mejor candidato.
                                           o
Ped´ a uno de los estudiantes m´s talentosos de Yorktown, Matt Ahrens, que
    ı                            a
probase Python. En dos meses, no s´lo hab´ aprendido el lenguaje, sino que
                                     o       ıa
escribi´ una aplicaci´n llamada pyTicket que permit´ a nuestro personal infor-
       o             o                             ıa
mar de problemas tecnol´gicos via Web. Sab´ que Matt no pod´ terminar una
                          o                 ıa                  ıa
aplicaci´n de tal escala en tan poco tiempo con C++, y este logro, combinado
        o
con la positiva valoraci´n de Python por parte de Matt, suger´ que Python era
                        o                                    ıa
la soluci´n que buscaba.
         o



Encontrar un libro de texto
Una vez decidido a usar Python tanto en mis clases de inform´tica b´sica como
                                                              a      a
en el a˜o siguiente, el problema m´s acuciante era la falta de un libro de texto
       n                          a
disponible.
El contenido libre vino al rescate. Anteriormente en ese a˜o, Richard Stallman
                                                            n
me present´ a Allen Downey. Ambos hab´
           o                                ıamos escrito a Richard expresando
nuestro inter´s en desarrollar conenidos educativos libres. Allen ya hab´ escrito
             e                                                          ıa
un libro de texto de inform´tica de primer a˜o, How to Think Like a Com-
                              a                 n
puter Scientist. Cuando le´ ese libro, supe inmediatamente que quer´ usarlo
                            ı                                           ıa
en mi clase. Era el libro de inform´tica m´s claro y pr´ctico que hab´ visto.
                                    a       a             a              ıa
Pon´ el ´nfasis en los procesos de pensamiento involucrados en la programaci´n
    ıa e                                                                      o
m´s que en las caracter´
  a                     ısticas de un lenguaje en particular. Su lectura me hizo
inmediatamente un maestro mejor.
How to Think Like a Computer Scientist no era s´lo un libro excelente, sino que
                                                  o
se public´ bajo la licencia p´blica GNU, lo que significaba que pod´ usarse y
          o                  u                                        ıa
modificarse libremente para ajustarse a las necesidades de su usuario. Una vez
que decid´ usar Python, se me ocurri´ que podr´ traducir la versi´n original
           ı                          o            ıa                 o
en Java del libro de Allen al nuevo lenguaje. Aunque no hubiera sido capaz de
escribir un libro de texto por mi cuenta, tener el libro de Allen para trabajar a
ix


partir de ´l me hizo posible hacerlo, mostrando al mismo tiempo que el modelo
          e
cooperativo de desarrollo que tan buenos resultados hab´ dado en el software
                                                        ıa
pod´ funcionar tambi´n para el contenido educativo.
    ıa                 e


El trabajo en este libro durante los dos ultimos a˜os ha sido gratificante para mis
                                          ´          n
estudiantes y para m´ y mis estudiantes desempe˜aron un importante papel en
                       ı,                              n
el proceso. Como pod´ hacer cambios instant´neos cuando alguien encontraba
                        ıa                         a
un error ortogr´fico o un pasaje dif´
                a                      ıcil, los anim´ a buscar errores en el libro
                                                       e
d´ndoles un punto extra cada vez que hac´ una sugerencia que terminaba
 a                                               ıan
como un cambio en el texto. Esto tuvo el doble beneficio de animarlos a leer el
texto con m´s atenci´n y tener el texto revisado en profundidad por sus cr´
            a         o                                                      ıticos
m´s importantes: los estudiantes que lo usan para aprender inform´tica.
  a                                                                   a


Para la segunda mitad del libro, acerca de la programaci´n orientada a objetos,
                                                        o
sab´ que necesitar´ a alguien con m´s experiencia real en programaci´n de
    ıa            ıa                   a                                 o
la que yo ten´ para hacerlo bien. El libro se estanc´ en un estado inacabado
             ıa                                      o
durante buena parte de un a˜o hasta que la comunidad de c´digo abierto de
                             n                                 o
nuevo proporcion´ los medios necesarios para su terminaci´n.
                o                                         o


Recib´ un correo electr´nico de Chris Meyers expresando su inter´s en el li-
      ı                o                                           e
bro. Chris es un programador profesional que empez´ a impartir un curso de
                                                     o
programaci´n con Python el a˜o pasado en el Colegio de Lane Community,
            o                   n
en Eugene, Oregon. La perspectiva de impartir el curso llev´ a Chris has-
                                                              o
ta el libro, y empez´ a colaborar con ´l inmediatamente. Hacia el final del
                     o                   e
a˜o escolar hab´ creado un proyecto complementario en nuesto sitio web en
 n              ıa
http://www.ibiblio.org/obp llamado Python for Fun y estaba trabajando
con algunos de mis estudiantes aventajados como profesor magistral, dirigi´ndo-
                                                                          e
les m´s all´ de donde yo pod´ llevarles.
     a     a                 ıa




Presentando la programaci´n con Python
                         o

El 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                                                   n
estudiantes 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                                                                       Prefacio

El 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 de
Inform´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
                                   a
ellos est´n sin duda un poco nerviosos, tras haber o´ que programar compu-
          a                                             ıdo
tadores es algo dif´ de aprender. La versi´n C++ siempre me ha obligado a
                    ıcil                      o
elegir entre dos opciones insatisfactorias: explicar las sentencias #include, void
main(), {, y } y arriesgarme a confundir o intimidar a algunos estudiantes desde
el principio, o decirles “No te preocupes de todo eso ahora, hablaremos de ello
m´s tarde”, y arriesgarme a lo mismo. Los objetivos educativos en este momento
  a
del 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                                                        a
el entorno de programaci´n. La programaci´n con Python tiene exactamente lo
                           o                  o
que necesito para hacer estas cosas, y nada m´s. a

La comparaci´n del texto explicativo de este programa para cada versi´n del
               o                                                          o
libro ilustra mejor lo que esto significa para los estudiantes principiantes. Hay
trece p´rrafos de explicaci´n de “¡Hola, mundo!” en la versi´n C++. En la
        a                   o                                    o
versi´n Python s´lo hay dos. A´n m´s importante: los once p´rrafos que faltan
     o            o             u     a                        a
no tocan las “grandes ideas” de la programaci´n de computadores, sino las
                                                  o
minucias de la sintaxis de C++. Encontr´ que esto mismo suced´ por todo el
                                          e                        ıa
libro. P´rrafos enteros desapareciendo de la versi´n Python del texto porque la
         a                                        o
sintaxis clara de Python los hace innecesarios.

El uso de un lenguaje de muy alto nivel como Python permite que el profesor
deje para m´s tarde hablar sobre los detalles de bajo nivel de la m´quina hasta
            a                                                       a
que los estudiantes tengan el fondo necesario para entender los detalles. De este
modo crea la habilidad de poner pedag´gicamente “antes lo primero”. Uno de
                                         o
los 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. Las
variables deben declararse seg´n su tipo en parte porque el tama˜o del lugar al
                               u                                  n
que 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
xi


lo 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               a
comprensi´n. En Python una variable es un nombre que se˜ala una cosa. Este
           o                                               n
es un concepto mucho m´s intuitivo para estudiantes principiantes y est´ m´s
                          a                                              a a
cerca del significado de “variable” que aprendieron en su clase de matem´ticas.
                                                                         a
Este a˜o tuve muchas menos dificultades ense˜ando lo que son las variables que
      n                                        n
en el anterior, y pas´ menos tiempo ayud´ndoles con los problemas derivados
                     e                     a
de su uso.
Otro ejemplo de c´mo Python ayuda en la ense˜anza y aprendizaje de la pro-
                    o                             n
gramaci´n es en su sintaxis para las funciones. Mis estudiantes siempre han
         o
tenido una gran dificultad comprendiendo las funciones. El problema principal
se centra alrededor de la diferencia entre la definici´n de una funci´n y la llama-
                                                     o              o
da a una funci´n, y la distinci´n asociada entre un par´metro y un argumento.
               o                o                         a
Python 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               o
estudiantes: “cuando definas una funci´n, empieza con def, seguido del nombre
                                        o
de 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
                                a
con las llamadas. No hay tipo de retorno, tipos de par´metros, o par´metro por
                                                        a             a
referencia y valor de por medio, por lo que ahora soy capaz de ense˜ar funciones
                                                                    n
en la mitad de tiempo que antes, con mejor comprensi´n.  o
El uso de Python ha mejorado la eficacia de nuestro programa de inform´ticaa
para todos los estudiantes. Veo un mayor nivel general de ´xito y un menor
                                                             e
nivel de frustraci´n del que experiment´ durante los dos a˜os que ense˜´ C++.
                  o                    e                  n           ne
Avanzo m´s r´pido con mejores resultados. M´s estudiantes terminan el curso
           a a                                a
con la habilidad de crear programas utiles y con la actitud positiva hacia la
                                      ´
experiencia de programaci´n que esto engendra.
                           o


Formar una comunidad
He recibido correos electr´nicos de todos los rincones del planeta de parte
                          o
de gente que usa este libro para aprender o enese˜ar a programar. Ha em-
                                                  n
pezando a surgir una comunidad de usuarios, y muchas personas han contri-
buido al proyecto mandando materiales a trav´s del sitio web complementario
                                             e
http://www.thinkpython.com.
Con la publicaci´n de este libro en forma impresa, espero que continue y se
                 o
acelere el crecimiento de la comunidad de usuarios. La emergencia de esta co-
munidad de usuarios y la posibilidad que sugiere para colaboraciones similares
entre educadores han sido para m´ las partes m´s excitantes de trabajar en este
                                 ı            a
proyecto. Trabajando juntos, podemos incrementar la calidad de los materiales
xii                                                                  Prefacio

disponibles para nuestro uso y ahorrar un tiempo valioso. Les invito a unirse a
nuestra comunidad y espero con impaciencia saber algo de ustedes. Por favor,
escriban a los autores a feedback@thinkpython.com.


Jeffrey Elkner
Escuela Secundaria Yortown
Arlington, Virginia
Lista de Colaboradores

Parafraseando la filosof´ de la Free Software Foundation, este libro es libre
                       ıa
como la libre expresi´n, pero no necesariamente gratis como la pizza gratis.
                     o
Se hizo realidad a causa de una colaboraci´n que no habr´ sido posible sin
                                           o              ıa
la GNU Free Documentation License. As´ que queremos agradecer a la Free
                                         ı
Software Foundation por desarrollar esta licencia y, por supuesto, ponerla a
nuestra disposici´n.
                 o
Tambi´n nos gustar´ dar las gracias a los m´s de cien lectores de aguda vista
       e            ıa                        a
que 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 nuestra
gratitud en la forma de una lista de colaboradores. Desgraciadamente, esta listo
no est´ completa, pero hacemos lo que podemos para mantenerla actualizada.
      a
Si se toma el tiempo de echar un vistazo a la lista, ver´ que cada una de las
                                                        a
personas que aparecen le ha ahorrado a usted y a los lectores que le sucedan
la confusi´n de un error t´cnico o una explicaci´n poco clara simplemente en-
          o               e                     o
vi´ndonos una nota.
  a
Pos imposible que parezca tras tantas correcciones, todav´ puede haber
                                                               ıa
errores en el libro. Si se encontrara con una, esperamos que se tome un
minuto para ponerse en contacto con nosotros. La direcci´n de correo es
                                                              o
feedback@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                      o
quedar 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
xv


Michael 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                      o
meHora 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 Perl
de Jonah Cohen que, a partir de L TEX genera, el HTML.
                                  A


Craig T. Snydal est´ poniendo a prueba el texto en un curso en la Uni-
                   a
versidad de Drew. Ha contribuido con varias sugerencias y correcciones de
importancia.

Ian Thomas y sus estudiantes usan el texto en un curso de programaci´n.
                                                                      o
Son los primeros en probar los Cap´
                                  ıtulos de la segunda mitad del libro, y
han hecho numerosas correcciones y sugerencias.

Keith Verheyden envi´ una correcci´n del Cap´
                    o             o         ıtulo 3.

Peter Winstanley nos hizo saber de un persistente error en nuestro lat´
                                                                      ın
del 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 a
de escribir el primer borrador del Cap´
                                      ıtulo sobre diccionarios, propor-
cion´ una gu´ continuada en las primeras etapas del libro.
    o        ıa

Christoph Zwerschke envi´ varias correcciones y sugerencias pedag´gicas,
                           o                                     o
y explic´ la diferencia entre gleich y selbe.
        o

James Mayer envi´ un cargamento de errores tipogr´ficos y ortogr´ficos,
                 o                               a             a
incluyendo dos en la lista de colaboradores.

Hayden McAfee pill´ una inconsistencia potencialmente confusa entre dos
                  o
ejemplos.
´
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                e
errores en la versi´n inglesa.
                   o

Tauhidul Hoque y Lex Berezhny crearon las ilustraciones del Cap´
                                                               ıtulo 1
y mejoraron muchas de las otras ilustraciones.

Dr. Michele Alzetta pill´ un error en el Cap´
                        o                    ıtulo 8 y envi´ varios comen-
                                                           o
tarios 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 general

Pr´logo
  o                                                                                    v



Prefacio                                                                             vii



Lista de Colaboradores                                                               xiii



1. 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .      9

2. 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .    19

3. 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .    32

4. 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .    45

5. 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .   58

6. 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 general

7. 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .    83

8. 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .    97

9. 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107

10.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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116

11.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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128

12.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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139

13.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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147

14.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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160

15.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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169

16.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 general

17.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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192

18.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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201

19.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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224


A. 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
                     a


B. 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 general

C. 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . 256

D. Lecturas recomendadas                                                       259
   D.1.   Libros y sitios web sobre Python . . . . . . . . . . . . . . . . . 260
   D.2.   Libros recomendados sobre inform´tica en general . . . . . . . . 261
                                          a

E. 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 1

El Camino del Programa

El objetivo de este libro es ense˜arle a pensar como lo hacen los cient´
                                 n                                         ıficos
inform´ticos. Esta manera de pensar combina las mejores caracter´
       a                                                           ısticas de la
matem´tica, la ingenier´ y las ciencias naturales. Como los matem´ticos, los
       a                ıa,                                          a
cient´
     ıficos inform´ticos usan lenguajes formales para designar ideas (espec´
                 a                                                         ıfica-
mente, computaciones). Como los ingenieros, ellos dise˜an cosas, ensamblando
                                                        n
sistemas a partir de componentes y evaluando ventajas y desventajas de cada
una de las alternativas. Como los cient´
                                       ıficos, ellos observan el comportamiento
de sistemas complejos, forman hip´tesis, y prueban sus predicciones.
                                  o

La habilidad m´s importante del cient´
                a                      ıfico inform´tico es la soluci´n de pro-
                                                  a                 o
blemas. La soluci´n de problemas incluye poder formular problemas, pensar en
                   o
la soluci´n de manera creativa, y expresar una soluci´n con claridad y precisi´n.
         o                                           o                        o
Como se ver´, el proceso de aprender a programar es la oportunidad perfecta
             a
para desarrollar la habilidad de resolver problemas. Por esa raz´n este cap´
                                                                 o          ıtulo
se 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                  u
resultado. Ese resultado se ver´ m´s claramente durante el proceso.
                               a a



1.1.     El lenguaje de programaci´n Python
                                  o
El lenguaje de programaci´n que aprender´ es Python. Python es un ejemplar
                         o              a
de un lenguaje de alto nivel; otros ejemplos de lenguajes de alto nivel son C,
C++, Perl y Java.
2                                                    El Camino del Programa

Como se puede deducir de la nomenclatura “lenguaje de alto nivel”, tambi´n    e
existen lenguajes de bajo nivel, a los que tambi´n se califica como lengua-
                                                     e
jes de m´quina o lenguajes ensambladores. A prop´sito, los computadores s´lo
         a                                          o                        o
ejecutan programas escritos en lenguajes de bajo nivel. Los programas de alto
nivel tienen que traducirse antes de ejecutarse. Esta traducci´n lleva tiempo, lo
                                                              o
cual es una peque˜a desventaja de los lenguajes de alto nivel.
                   n

Aun as´ las ventajas son enormes. En primer lugar, la programaci´n en lenguajes
       ı                                                         o
de alto nivel es mucho m´s f´cil; escribir programas en un lenguaje de alto nivel
                         a a
toma menos tiempo, los programas son m´s cortos y m´s f´ciles de leer, y es m´s
                                           a           a a                     a
probable que estos programas sean correctos. En segundo lugar, los lenguajes
de alto nivel son portables, lo que significa que pueden ejecutarse en tipos
diferentes de computadores sin modificaci´n alguna o con pocas modificaciones.
                                            o
Los programas escritos en lenguajes de bajo nivel s´lo pueden ser ejecutarse en
                                                    o
un tipo de computador y deben reescribirse para ejecutarlos en otro.

Debido a estas ventajas, casi todos los programa se escriben en un lenguaje de
alto nivel. Los lenguajes de bajo nivel s´lo se usan para unas pocas aplicaciones
                                         o
especiales.

Hay dos tipos de programas que traducen lenguajes de alto nivel a lenguajes
de bajo nivel: int´rpretes y compiladores. Un int´rprete lee un programa de
                    e                                   e
alto 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
              FUENTE



Un compilador lee el programa y lo traduce todo al mismo tiempo, antes de
ejecutar cualquiera de las instrucciones. En este caso, al programa de alto nivel
se le llama el c´digo fuente, y al programa traducido el c´digo de obje-
                o                                               o
to o el c´digo ejecutable. Una vez compilado el programa, puede ejecutarlo
         o
repetidamente sin volver a traducirlo.


CODIGO                               CODIGO
                COMPILADOR                            EJECUTOR             SALIDA
FUENTE                               OBJETO



Python 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                                                                         3

int´rprete: modo de comando y modo de gui´n. En modo de comando se escri-
   e                                         o
ben sentencias en el lenguaje Python y el int´rprete muestra el resultado.
                                             e
$ python
Python 1.5.2 (#1, Feb 1 2000, 16:32:16)
Copyright 1991-1995 Stichting Mathematish Centrum, Amsterdam
>>> print 1 + 1
2
La primera l´
            ınea de este ejemplo es el comando que pone en marcha el int´rprete
                                                                          e
Python. Las dos l´ıneas siguientes son mensajes del int´rprete. La tercera l´
                                                        e                    ınea
comienza con >>>, que es la invitaci´n del int´rprete para indicar que est´ listo.
                                     o        e                           a
Escribimos print 1 + 1 y el int´rprete contest´ 2.
                                 e               o
Alternativamente, se puede escribir el programa en un archivo y usar el int´rpre-
                                                                           e
te 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
           o
latoya.py que contenga esta l´ ınea:
print 1 + 1
Por acuerdo un´nime, los archivos que contienen programas de Python tienen
              a
nombres que terminan con .py.
Para ejecutar el programa, se le tiene que indicar el nombre del gui´n al int´rpre-
                                                                    o        e
te.
$ python latoya.py
2
En otros entornos de desarrollo los detalles de la ejecuci´n de programas pueden
                                                          o
ser diferentes. Aem´s, la mayor´ de programas son m´s interesantes que el
                   a              ıa                        a
mencionado.
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 para
pruebas r´pidas porque se pueden teclear las instrucciones de Python y se pue-
         a
den ejecutar inmediatamente. Una vez que un programa est´ completo, puede
                                                            a
archivarse en un gui´n para ejecutarlo o modificarlo en el futuro.
                    o


1.2.     ¿Qu´ es un programa?
            e
Un programa es una secuencia de instrucciones que especifican c´mo ejecutar
                                                              o
una computaci´n. La computaci´n puede ser algo matem´tico, como solucionar
             o               o                       a
4                                                 El Camino del Programa

un sistema de ecuaciones o determinar las ra´ de un polinomio, pero tambi´n
                                            ıces                          e
puede ser una computaci´n simb´lica, como buscar y reemplazar el texto de un
                        o       o
documento o (aunque parezca raro) compilar un programa.

Las instrucciones (comandos, ´rdenes) tienen una apariencia diferente en len-
                              o
guajes de programaci´n diferentes, pero existen algunas funciones b´sicas que
                      o                                            a
se 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.
                   o

operaci´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.
            o

Lo crea o no, eso es todo. Todos los programas que existen, por complicados que
sean, 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
                           o
vez m´s peque˜as hasta que estas tareas sean suficientemente simples para ser
      a         n
ejecutadas con una de estas instrucciones simples.

Quiz´s esta descripci´n sea un poco ambigua. No se preocupe. Lo explicaremos
    a                o
con m´s detalle con el tema de los algoritmos.
      a



1.3.     ¿Qu´ es la depuraci´n (debugging)?
            e               o
La programaci´n es un proceso complejo y, por ser realizado por humanos, a
              o
menudo desemboca en errores. Por razones caprichosas, esos errores se llaman
bugs 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, en
tiempo 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                                                        5

1.3.1.    Errores sint´cticos
                      a

Python s´lo puede ejecutar un programa si el programa es correcto sint´ctica-
          o                                                                a
mente. En caso contrario, es decir si el programa no es correcto sint´cticamente,
                                                                     a
el proceso falla y devuelve un mensaje de error. El t´rmino sintaxis se refiere a
                                                     e
la 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               u
oraciones deben terminar con un punto. esta oraci´n tiene un error sint´ctico.
                                                   o                      a
Esta oraci´n tambi´n
           o        e

Para la mayor´ de lectores, unos pocos errores sint´cticos no son significatvos,
              ıa                                   a
y por eso pueden leer la poes´ de e. e. cummings sin anunciar errores de sin-
                              ıa
taxis. Python no es tan permisivo. Si hay aunque sea un solo error sint´ctico
                                                                         a
en el programa, Python mostrar´ un mensaje de error y abortar´ la ejecuci´n
                                 a                               a           o
del programa. Durante las primeras semanas de su carrera como programador
pasar´, seguramente, mucho tiempo buscando errores sint´cticos. Sin embargo,
      a                                                   a
tal como adquiera experiencia tendr´ menos errores y los encontrar´ mas r´pido.
                                   a                              a      a



1.3.2.    Errores en tiempo de ejecuci´n
                                      o

El segundo tipo de error es un error en tiempo de ejecuci´n. Este error no aparece
                                                         o
hasta que se ejecuta el programa. Estos errores tambi´n se llaman excepciones
                                                       e
porque indican que algo excepcional (y malo) ha ocurrido.

Con los programas que vamos a escribir al principio, los errores en tiempo de
ejecuci´n ocurrir´n con poca frecuencia, as´ que puede pasar bastante tiempo
       o         a                         ı
hasta que vea uno.



1.3.3.    Errores sem´nticos
                     a

El tercer tipo de error es el error sem´ntico. Si hay un error de l´gica en su
                                        a                          o
programa, el programa se ejecutar´ sin ning´n mensaje de error, pero el resul-
                                   a        u
tado no ser´ el deseado. Ser´ cualquier otra cosa. Concretamente, el programa
            a                 a
har´ lo que usted le dijo.
   a

A veces ocurre que el programa escrito no es el programa que se ten´ en mente.
                                                                     ıa
El sentido o significado del programa (su sem´ntica) no es correcto. Es dif´ ha-
                                              a                             ıcil
llar errores de l´gica, porque requiere trabajar al rev´s, observando el resultado
                 o                                     e
del programa para averiguar lo que hace.
6                                                   El Camino del Programa

1.3.4.    Depuraci´n experimental
                  o
Una de las t´cnicas m´s importantes que usted aprender´ es la depuraci´n. Aun-
             e         a                                   a              o
que a veces es frustrante, la depuraci´n es una de las partes m´s intelectualmente
                                      o                        a
ricas, interesantes y estimulantes de la programaci´n.
                                                     o
La depuraci´n es una actividad parecida a la tarea de un investigador: se tie-
            o
nen que estudiar las claves para inducir los procesos y eventos llevaron a los
resultados que tiene a la vista.
La depuraci´n tambi´n es una ciencia experimental. Una vez que se tiene la
             o         e
idea de cu´l es el error, se modifica el programa y se intenta nuevamente. Si su
           a
hip´tesis fue la correcta se pueden predecir los resultados de la modificaci´n y
    o                                                                        o
estar´ m´s cerca de un programa correcto. Si su hip´tesis fue err´nea tendr´ que
      a a                                           o            o          a
idearse otra hip´tesis. Como dijo Sherlock Holmes, “Cuando se ha descartado
                 o
lo 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              o
gramaci´n es el proceso de depurar un programa gradualmente hasta que haga
        o
lo que usted quiera. La idea es que deber´ usted comenzar con un programa
                                         ıa
que haga algo y hacer peque˜as modificaciones, depur´ndolas sobre la marcha,
                            n                       a
de modo que siempre tenga un programa que funcione.
Por ejemplo, Linux es un sistema operativo que contiee miles de l´
                                                                 ıneas de c´digo,
                                                                           o
pero Linus Torvalds lo comenz´ como un programa para explorar el microproce-
                              o
sador Intel 80836. Seg´n Larry Greenfield, “Uno de los proyectos tempranos de
                      u
Linus fue un programa que alternaba la impresi´n de AAAA con BBBB. Este
                                                o
programa evolucion´ en Linux” (de The Linux Users’Guide Versi´n Beta 1).
                    o                                              o
Otros cap´
         ıtulos tratar´n m´s acerca del tema de depuraci´n y otras t´cnicas de
                      a   a                             o           e
programaci´n.
           o



1.4.     Lenguajes formales y lenguajes naturales
Los lenguajes naturales son los lenguajes hablados por seres humanos, como
el espa˜ol, el ingl´s y el franc´s. No los han dise˜ados personas (aunque se
       n           e            e                    n
intente poner cierto orden en ellos), sino que se han desarrollado naturalmente.
Los lenguajes formales son lenguajes dise˜ados por humanos y que tienen
                                                n
aplicaciones espec´ıficas. La notaci´n matem´tica, por ejemplo, es un lenguaje
                                    o          a
formal ya que se presta a la representaci´n de las relaciones entre n´meros y
                                           o                             u
s´
 ımbolos. Los qu´ımicos utilizan un lenguaje formal para representar la estructura
qu´ı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-
                                                      a
plo, 3 + 3 = 6 es una expresi´n matem´tica correcta, pero 3 = +6$ no lo es. De
                             o         a
la misma manera, H2 0 es una nomenclatura qu´    ımica correcta, pero 2 Zz no lo
es.

Existen dos clases de reglas sint´cticas, en cuanto a unidades y estructura. Las
                                 a
unidades son los elementos b´sicos de un lenguaje, como lo son las palabras, los
                              a
n´meros y los elementos qu´
  u                          ımicos. Por ejemplo, en 3=+6$, $ no es una unidad
matem´tica aceptada (al menos hasta donde nosotros sabemos. Similarmente,
       a
2 Zz no es formal porque no hay ning´ n elemento con la abreviatura Zz.
                                       u

La segunda clase de regla sint´ctica est´ relacionada con la estructura de un
                               a        a
elemento; 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 el
s´                                   o
n´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.
      a

Al leer una oraci´n, sea en un lenguaje natural o una sentencia en un lenguaje
                 o
t´cnico, se debe discernir la estructura de la oraci´n. En un lenguaje natural
 e                                                  o
este proceso, llamado an´lisis sint´ctico ocurre subconscientemente.
                         a          a

Por ejemplo cuando usted escucha la oraci´n “El otro zapato cay´”, entiende
                                            o                       o
que “el otro zapato” es el sujeto y “cay´” es el verbo. Cuando se ha analizado
                                        o
la oraci´n sint´cticamente, se puede deducir el significado, o la sem´ntica, de la
        o      a                                                    a
oraci´n. Suponiendo que sepa lo ques es un zapato y lo que es caer, entender´ el
     o                                                                       a
significado de la oraci´n.
                      o

Aunque existen muchas cosas en com´n entre los lenguajes naturales y los
                                    u
lenguajes formales—por ejemplo las unidades, la estructura, la sintaxis y la
sem´ntica—tambi´n existen muchas diferencias:
    a            e

ambig¨ 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 Programa

redundancia: 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.
                                                       a

literalidad: 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 veces
la diferencia entre los lenguajes formales y los naturales es comparable a la
diferencia 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                        o

Prosa: 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                 u

Programas: 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
                                                                      a
que los lenguajes naturales, y por consecuente lleva m´s tiempo leerlos. Tam-
                                                        a
bi´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 la
estructura. Finalmente, ponga atenci´n a los detalles. Los fallos de puntuaci´n
                                      o                                      o
y la ortograf´ que puede obviar en el lenguaje natural, pueden suponer una
              ıa,
gran diferencia en un lenguaje formal.



1.5.     El primer programa
Tradicionalmente el primer programa en un lenguaje nuevo se llama “Hola,
mundo” (Hello world!) porque s´lo muestra las palabras “Hola a todo el mundo”.
                              o
En Python es as´
               ı:

print "Hola, mundo"
1.6 Glosario                                                                    9

Este es un ejemplo de una sentencia print, la cual no imprime nada en papel,
m´s bien muestra un valor. En este caso, el resultado es las palabras
 a

Hola, mundo

Las comillas se˜alan el comienzo y el final del valor; no aparecen en el resultado.
               n
Alguna gente eval´a la calidad de un lenguaje de programaci´n por la simplici-
                 u                                         o
dad del programa “Hola, mundo”. Si seguimos ese criterio, Python cumple con
todas sus metas.



1.6.     Glosario
soluci´n de problemas: El proceso de formular un problema, hallar la solu-
      o
     ci´n y expresar esa soluci´n.
       o                       o
lenguaje 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.
      a
interpretar: Ejecutar un programa escrito en un lenguaje de alto nivel tradu-
     ci´ndolo l´
       e       ınea por l´
                         ınea

compilar: 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).
   o

programa: Un conjunto de instrucciones que especifica una computaci´n.
                                                                  o
algoritmo: Un proceso general para resolver una clase completa de problemas.
10                                               El Camino del Programa

error (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.
                 o

sintaxis: 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).
                             a

error 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.
                                                                 u

excepci´n: Otro nombre para un error en tiempo de ejecuci´n.
       o                                                 o

error 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.
   a

language 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.
          a

an´lisis sint´ctico: La examinaci´n de un programa y el an´lisis de su estruc-
  a          a                   o                        a
     tura sint´ctica.
              a

sentencia print: Una instrucci´n que causa que el int´rprete Python muestre
                              o                      e
     un valor en el monitor.
Cap´
   ıtulo 2

Variables, expresiones y
sentencias

2.1.     Valores y tipos
El valor es uno de los elementos fundamentales (como por ejemplo una letra o
un n´mero) que manipula un programa. Los valores que hemos visto hasta el
    u
momento 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 una
cadena, llamada as´ porque contiene una “cadena” de letras. Usted (y el
                    ı
int´rprete) puede identificar las cadenas porque est´n encerradas entre comi-
   e                                               a
llas.
La sentencia print tambi´n funciona con enteros:
                        e
>>> print 4
4
Si no est´ seguro del tipo que tiene un determinado valor, puede pregunt´rselo
         a                                                              a
al 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
                                                                         e
los enteros sean de tipo int (por integer en ingl´s). De forma menos obvia, los
                                                 e
12                                           Variables, expresiones y sentencias

n´meros con decimales (separados por medio de un punto en ingl´s) son de tipo
 u                                                            e
float debido a la representaci´n de estos n´meros en el formato llamado de
                              o            u
coma 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               a
entre 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
                                    ıa
de tres d´                          ´
         ıgitos, como en 1,000,000. Este no es un entero legal en Python, pero
es una expresi´n legal:
               o

>>> print 1,000,000
1 0 0

En fin, no era eso lo que quer´ ıamos. Python interpreta 1,000,000 como una
lista de tres n´meros que debe imprimir. As´ que recuerde no insertar comas en
               u                           ı
sus enteros. 1



2.2.       Variables
Una de las caracter´ ısticas m´s potentes de los lenguajes de programaci´n es
                              a                                         o
la capacidad de manipular variables. Una variable es un nombre que hace
referencia a un valor.

La sentencia de asignaci´n crea nuevas variables y les asigna un valor:
                        o

>>> mensaje = "Que onda?"
>>> n = 17
>>> pi = 3.14159

Este 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                          n
apunt´ en una nota anterior
     o
2.3 Nombres de variables y palabras reservadas                                  13

valor entero 17 a n, y la tercera le da el valor de n´mero en coma flotante
                                                     u
3.14159 a pi.

Una forma habitual de representar variables sobre el papel es escribir el nombre
con una flecha se˜alando al valor de la variable. Este tipo de representaci´n
                 n                                                            o
se llama diagrama de estado, ya que muestra en qu´ estado se halla cada
                                                        e
una de las variables (consid´relo como el “estado de ´nimo” de la variable”).
                            e                         a
El siguiente diagrama muestra el efecto de las tres sentencias de asignaci´n  o
anteriores:

                       mensaje          "Que onda?"
                               n        17
                              pi        3.14159

La sentencia print tambi´n funciona con variables.
                        e

>>> print mensaje
"Que onda?"
>>> print n
17
>>> print pi
3.14159

En cada caso, el resultado es el valor de la variable. Las variables tambi´n tienen
                                                                          e
tipo. 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 reservadas
Como norma general, los programadores eligen nombres significativos para sus
variables: esto permite documentar para qu´ se usa la variable.
                                          e

Los nombres de las variables pueden tener una longitud arbitraria. Pueden estar
formados por letras y n´meros, pero deben comenzar con una letra. Aunque es
                       u
14                                      Variables, expresiones y sentencias

aceptable usar may´sculas, por convenci´n no lo hacemos. Si lo hace, recuerde
                    u                   o
que la distinci´n es importante: Bruno y bruno son dos variables diferentes.
               o
El gui´n bajo ( ) tambi´n es legal y se utiliza a menudo para separar nombres
      o                 e
con m´ltiples palabras, como mi nombre o precio del cafe colombiano.
      u
Si intenta darle a una variable un nombre ilegal, obtendr´ un error de sintaxis.
                                                         a
>>> 76trombones = "gran desfile"
SyntaxError: invalid syntax
>>> mas$ = 1000000
SyntaxError: invalid syntax
>>> class = "Curso de Programaci´n 101"
                                 o
SyntaxError: invalid syntax
76trombones es ilegal porque no comienza por una letra. mas$ es ilegal porque
contiene un car´cter ilegal, el signo del d´lar. Pero ¿qu´ tiene de malo class?
               a                           o             e
Resulta que class es una de las palabras reservadas de Python. El lenguaje
usa las palabras reservadas para definir sus reglas y estructura, y no pueden
usarse 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        while
Tal vez quiera mantener esta lista a mano. Si el int´rprete se queja de alguno
                                                     e
de sus nombres de variable, y usted no sabe por qu´, compruebe si est´ en esta
                                                   e                  a
lista.


2.4.     Sentencias
Una sentencia es una instrucci´n que puede ejecutar el int´rprete de Python.
                              o                             e
Hemos visto dos tipos de sentencias: print y la asignaci´n.
                                                        o
Cuando usted escribe una sentencia en la l´  ınea de comandos, Python la ejecuta
y 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                    u
Normalmente un gui´n contiene una secuencia de sentencias. Si hay m´s de una
                      o                                               a
sentencia, los resultados aparecen de uno en uno tal como se van ejecutando las
sentencias.
2.5 Evaluar expresiones                                                        15

Por ejemplo, el gui´n
                   o

print 1
x = 2
print x

prsenta la salida

1
2

De nuevo, la sentencia de asignaci´n no produce ninguna salida.
                                  o



2.5.      Evaluar expresiones
Una expresi´n es una combinaci´n de valroes, variables y operadores. Si teclea
           o                   o
una expresi´n en la l´
           o         ınea de comandos, el int´rprete la eval´ a y muestra el
                                             e               u
resultado:

>>> 1 + 1
2

Un valor, y tambi´n una variable, se considera una expresi´n por s´ mismo.
                 e                                        o       ı

>>> 17
17
>>> x
2

Para complicar las cosas, evaluar una expresi´n no es del todo lo mismo que
                                             o
imprimir un valor.

>>> mensaje = "Que onda?"
>>> mensaje
"Que onda?"
>>> print mensaje
Que onda?

Cuando Python muestra el valor de una expresi´n, usa el mismo formato que
                                                  o
usted usar´ para introducir un valor. En el caso de las cadenas, eso significa que
           ıa
incluye las comillas. Pero la sentencia print imprime el valor de la expresi´n, lo
                                                                            o
que 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                    a
El gui´n
      o
16                                     Variables, expresiones y sentencias

17
3.2
"Hola, mundo"
1 + 1

no presenta ninguna salida. ¿C´mo cambiar´ usted el gui´n para mostrar los
                                o        ıa            o
valores de estas cuatro expresiones?



2.6.     Operadores y expresiones
Los operadores son s´ ımbolos especiales que representan c´lculos simples, como
                                                          a
la suma y la multiplicaci´n. Los valores que usa el operador se llaman operan-
                         o
dos.

Las siguientes expresione son legales en Python y su significado es m´s o menos
                                                                    a
claro:

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
                                         e
todos de la misma forma que en matem´ticas. El asterisco (*) es el signo de
                                         a
multiplicaci´n y ** el s´
            o           ımbolo para exponenciaci´n.
                                                 o

Cuando aparece el nombre de una variable en el lugar de un operando, se sus-
tituye con su valor antes de realizar la operaci´n.
                                                o

La suma, resta, multiplicaci´n y exponenciaci´n hacen lo esperado, pero la divi-
                            o                o
si´n le puede sorprender. La operaci´n que sigue tiene un resultado inesperado:
  o                                 o

>>> minuto = 59
>>> minuto/60
0

El valor de la variable minuto es 59, y 59 dividido entre 60 es 0.98333 y no
0. El motivo de la discrepancia reside en que Python est´ llevando a cabo una
                                                        a
divisi´n de enteros.
      o

Cuando ambos operandos son enteros, el resultado ha de ser tambi´n un entero;
                                                                  e
por convenci´n, la divisi´n de enterios simpre se redondea a la baja, incluso en
            o            o
casos como estos en los que el siguiente entero est´ muy pr´ximo.
                                                   a       o

Una alternativa posible en este caso es el c´lculo de un porcentaje y no el de
                                            a
una fracci´n:
          o

>>> minuto*100/60
98
2.7 El orden de las operaciones                                               17

De nuevo se redondea el resultado a la baja, pero al menos ahora la respuesta
es aproximadamente correcta. Otra alternativa es la divisi´n de coma flotante,
                                                          o
que veremos en el Cap´
                     ıtulo 3.



2.7.     El orden de las operaciones
Cuando aparece m´s de un operador en una expresi´n, el orden de evaluaci´n
                   a                             o                      o
depende de las reglas de precedencia. Python sigue las mismas reglas de
precedencia que los propios matem´ticos para sus operaciones matem´ticas.
                                  a                                 a
Los ingleses usan el acr´nimo PEMDAS como regla parea recordar el orden de
                        o
las 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.
                    ıa



2.8.     Las operaciones sobre cadenas
En general no es posible realizar operaciones matem´ticas con cadenas, incluso si
                                                   a
las cadenas parecen n´meros. Las siguientes sentencias son ilegales (suponiendo
                      u
que mensaje sea de tipo string)

 mensaje-1      "Hola"/123      mensaje*"Hola"      "15"+2
18                                      Variables, expresiones y sentencias

Es curioso que el operador + funcione con cadenas, aunque no haga exactamente
lo 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                                                       e
extremo. Por ejemplo:

fruta = "pl´tano"
           a
bizcochoBueno = " pan de leche"
print fruta + bizcochoBueno

La salida del programa es pl´tano pan de leche. El espacio delante de pan
                             a
es parte de la cadena, y es necesario para introducir el espacio que separa las
cadenas concatenadas.
El operador * tambi´n funciona con cadenas; lleva a cabo la repetici´n. Por
                     e                                              o
ejemplo ’Chiste’*3 es ’ChisteChisteChiste’. Uno de los operandos ha de
ser una cadena, el otro ha de ser un entero.
Por un lado, esta interpretaci´n de + y * cobra sentido por analog´ con la
                                 o                                     ıa
suma y la multimplicaci´n. Igual que 4*3 es equivalente a 4+4+4, esperamos
                           o
que ’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              o
de la adici´n y la multiplicaci´n de enteros. ¿Puede encontrar una propiedad que
           o                   o
tienen la suma y la multiplicaci´n de enteros y que no tengan la concatenaci´n
                                  o                                           o
y la repetici´n de cadenas?
             o



2.9.     Composici´n
                  o
Hasta ahora hemos examinado los elementos de un programa (variables, expre-
siones y sentencias) por separado, sin hablar de c´mo combinarlos.
                                                  o
Una de las caracter´ısticas m´s utiles de los lenguajes de programaci´n es su
                              a ´                                    o
capacidad de tomar peque˜os bloques de construcci´n y ensamblarlos. Por
                            n                        o
ejemplo, sabemos c´mo sumar n´meros y c´mo imprimirlos; resulta que pode-
                   o            u           o
mos hacer las dos cosas a un tiempo:

>>>    print 17 + 3
20

En realidad, no debemos decir “al mismo tiempo”, puesto que en realidad la
suma tiene que realizarse antes de la impresi´n, pero la cuesti´n es que cualquier
                                             o                 o
expresi´n relacionada con n´meros, cadenas y variables puede usarse dentro de
       o                    u
una 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                                                          19

Y tambi´n puede poner expresiones arbitrarias en la parte derecha de una sen-
        e
tencia de asignaci´n:
                  o

porcentaje = (minuto * 100) / 60

Esta capacidad puede no resultar muy sorprendente, pero ya veremos otros
ejemplos donde la composici´n hace posible expresar c´lculos complejos con
                           o                         a
limpieza y brevedad.
         ´
ATENCION: Hay l´   ımites al lugar donde pueden usarse ciertas expresiones. Por
ejemplo, la parte izquierda de una sentencia de asignaci´n tiene que ser un
                                                             o
nombre de variable, no una exrpresi´n. Por tanto es ilegal lo siguiente: minute+1
                                    o
= hour.



2.10.      Los comentarios
Conforme los programas van creciendo de tama˜o y complic´ndose, se vuelven
                                               n           a
m´s complicados de leer. Los lenguajes formales son densos y con frecuencia es
  a
dif´ observar un trozo de c´digo y averiguar lo que hace, o por qu´ lo hace.
   ıcil                     o                                      e

Por ello es una buena idea a˜adir notas a su programa que expliquen, en un
                              n
lenguaje natural, qu´ hace el programa. Estas notas se llaman comentarios y
                    e
se marcan con el s´
                  ımbolo #:

# calcula el porcentaje de la hora que ha pasado ya
porcentaje = (minuto * 100) / 60

En este caso, el comentario aparece en una l´
                                            ınea propia. Tambi´n puede poner
                                                              e
comentarios al final de otras l´
                              ıneas:

porcentaje = (minuto * 100) / 60             # ojo: divisi´n de enteros
                                                          o

Todo lo que va del # al final de la l´ınea se ignora (no tiene efecto sobre el
programa). El mensaje est´ destinado al programador, o a futuros programa-
                         a
dores que podr´ tener que usar el c´digo. En este caso avisa al lector sobre el
              ıan                  o
sorprendente comportamiento de la divisi´n de enteros.
                                        o



2.11.      Glosario
valor: 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 sentencias

tipo: 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).
             u
coma flotante: un formato para representar n´meros con decimales.
                                           u
variable: 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.
        o
diagrama 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                  o
operando: uno de los valores sobre los que act´a un operador.
                                              u
expresi´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.
                          u
concatenar: 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.
     a
comentario: 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 3

Funciones

3.1.     Llamadas a funciones
Ya 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.
                       o
El valor o variable, llamado el argumento de la funci´n, ha de estar encerrado
                                                       o
entre 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          o
y devuelve un entero que act´a como identificador unico de ese valor.
                            u                    ´
  >>> id(3)
  134882108
  >>> yanira = 3
  >>> id(yanira)
  134882108
Cada valor tiene un id, que es un valor unico relacionado con d´nde se almacena
                                        ´                      o
en la memoria del computador. El id de una variable es el id del valor al que
hace referencia.
22                                                                    Funciones

3.2.     Conversi´n de tipos
                 o
Python proporciona una colecci´n de funciones internas que convierten valores
                                 o
de un tipo a otro. La funci´n int toma un valor y lo convierte a un entero, si
                             o
es posible, o da un error si no es posible.
  >>> int("32")
  32
  >>> int("Hola")
  ValueError: invalid literal for int(): Hola
int tambi´n convierte valores de coma flotante a enteros, pero recuerde que
          e
siempre redondea hacia abajo:
  >>> int(3.99999)
  3
La funci´n float que convierte enteros y cadenas en n´meros en coma flotante:
        o                                            u
  >>> float(32)
  32.0
  >>> float("3.14159")
  3.14159
Finalmente, 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
                       n
de coma flotante 1.0. Tal vez representen el mismo n´mero, pero pertenecen
                                                        u
a tipos distintos. El motivo es que se representan de forma distinta dentro del
computador.



3.3.     Coerci´n de tipos
               o
Ahora que ya sabemos convertir entre tipos, tenemos otra forma de enfrentarnos
a la divisi´n de enteros. Volviendo al ejemplo del cap´
           o                                           ıtulo anterior, suponga que
queremos calcular qu´ fracci´n de una hora hab´ transcurrido. La expresi´n
                       e       o                    ıa                          o
m´s obvia, minuto / 60, realiza una divisi´n de enteros, por lo que el resultado
  a                                         o
es siempre 0, incluso 59 minutos despu´s de la hora.
                                         e

Una alternativa es convetir minuto a tipo float (coma flotante) y luego efectuar
una divisi´n de coma flotante:
          o
3.4 Funciones matem´ticas
                   a                                                        23

  >>> minuto = 59
  >>> float(minuto) / 60.0
  0.983333333333
O bien podemos sacar provecho de las reglas de la conversi´n autom´tica de
                                                             o        a
tipos, llamada coerci´n de tipos. Para los operadores matem´ticos, si uno de
                     o                                         a
los operandos matem´ticos es tipo float, el otro se convierte autom´ticamente
                    a                                              a
en float.
  >>> minuto = 59
  >>> minuto / 60.0
  0.983333333333
Al usar un denomidador que es float, obligamos a Python a hacer divisi´n de
                                                                      o
coma flotante.



3.4.     Funciones matem´ticas
                        a
Es posible que ya haya visto usted en matem´ticas funciones como sin (seno) y
                                             a
log, 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           e
es 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      ı                a
a 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-
                                                                     a
plicadas 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                           ıa
funciones matem´ticas habituales. Un m´dulo es un archivo que contiene una
                 a                       o
colecci´n de funciones agrupadas juntas.
       o

Antes de poder usar las funciones de un m´dulo, tenemos que importarlo:
                                         o
  >>>import math
Para llamar a una de las funciones, tenemos que especificar el nombre del m´dulo
                                                                          o
y el nombre de la funci´n, separados por un punto. A esto se le llama notaci´n
                       o                                                     o
de punto:
  decibelio = math.log10 (17.0)
  angulo = 1.5
  altura = math.sin(angulo)
24                                                                     Funciones

La 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            o

La tercera sentencia halla el seno del valor de la variable angulo. sin y las otras
funciones trigonom´tricas (cos, tan, etc.) toman sus argumentos en radianes.
                   e
Para convertir de grados a radianes, puede dividir por 360 y multiplicar por
2*pi. Por ejemplo, para hallar el seno de 45 grados, calcule primero el ´ngulo
                                                                            a
en 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                     ız
2.
  >>> math.sqrt(2) / 2.0
  0.707106781187



3.5.     Composici´n
                  o
Igual que con las funciones matem´ticas, las funciones de Python se pueden
                                   a
componer; eso quiere decir que se usa una expresi´n como parte de otra. Por
                                                 o
ejemplo, 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
                                                              n
al valor de angulo. La suma se pasa luego como argumento a la funci´n cos.
                                                                      o

Tambi´n puede tomar el resultado de una funci´n y pas´rselo como argumento
       e                                     o       a
a otra:
  x = math.exp(math.log(10.0))
Esta sentencia encuentra el logaritmo en base e de 10 y luego eleva e a ese
exponente. El resultado queda asignado a x.



3.6.     A˜ adir funciones nuevas
          n
Hasta ahora s´lo hemos usado las funciones que vienen incluidas con Python,
              o
pero tambi´n es posible a˜adir nuevas funciones. La creaci´n de nuevas funciones
           e             n                                o
para 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                                                                       25

En contextos de programaci´n, funci´n es una secuencia de instrucciones con
                             o        o
nombre, que lleva a cabo la operaci´n deseada. Esta operaci´n se especifica en
                                    o                         o
una definici´n de funci´n. Las funciones que hemos usado hsta ahora las han
            o            o
definido por nosotros, y esas definiciones est´n ocultas. Eso es bueno, ya que nos
                                            a
permite 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 ):
     SENTENCIAS
Puede inventarse el nombre que desee para su funci´n, con la excepci´n de
                                                      o                 o
que no puede usar las palabras reservadas de Python. La lista de par´metros
                                                                      a
especifica qu´ informaci´n, en caso de haberla, ha de proporcionar para usar la
             e          o
funci´n nueva.
     o
Puede haber cualquier n´mero de sentencias dentro de la funci´n, pero tienen
                        u                                      o
que estar indentadas desde el margen izquierdo. En los ejemplos de este libro se
usar´ una indentaci´n de dos espacios.
    a              o
El primer par de funciones que escribiremos no tienen par´metros, de manera
                                                             a
que su sintaxis es:
  def nueva_linea():
     print
Esta funci´n se llama nueva linea. Los par´ntesis vac´ indican que no tiene
           o                                  e          ıos
par´metros. Contiene una unica sentencia, que muestra como salida un car´cter
   a                        ´                                              a
de 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
                              o
para 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       a
espacios, entre las l´
                     ıneas, ¿qu´ har´
                               e    ıamos? Podemos llamar varias veces a la
misma funci´n:
            o
  print "Primera linea."
  nueva_linea()
  nueva_linea()
  nueva_linea()
  print "Segunda linea."
26                                                                  Funciones

O bien podemos escribir una nueva funci´n que llamaremos tresLineas, y que
                                       o
imprima 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                                          a
espacios. Puesto que la siguiente sentencia no est´ indentada, Python sabe que
                                                   a
no es parte de la funci´n.
                       o
Observe 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
                                                  e
estas funciones nuevas. En realidad hay much´
                                            ısimas razones, pero este ejemplo
demuestra 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 uso
Juntando los fragmentos de c´digo de la secci´n anterior, el programa completo
                            o                o
queda 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 y
tresLineas. 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
                                             o
la funci´n no se ejecutan hasta que se llama a la funci´n, y la definici´n de la
        o                                              o               o
funci´n no genera salida.
      o
Como era de esperar, tiene que crear una funci´n antes de poder ejecutarla.
                                                 o
En otras palabras, la definici´n de la funci´n tiene que ejecutarse antes de la
                             o             o
primera 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
                         o
Para asegurarse de que una funci´n se define antes de su primer uso, tiene que
                                  o
conocer el orden en el que se ejecutan las sentencias; a esto se le llama flujo de
ejecuci´n.
       o
La ejecuci´n comienza siempre por la primera sentencia del programa. Las sen-
           o
tencias se ejecutan a raz´n de una cada vez, en orden, hasta que se alcanza una
                         o
llamada a una funci´n.
                     o
Las definiciones de funciones no alteran el flujo de ejecuci´n del programa, pero
                                                          o
recuerde que las sentencias que hay dentro de la funci´n no se ejecutan hasta
                                                        o
que se hace la llamada a la funci´n. Aunque no es habitual, puede definir una
                                 o
28                                                                    Funciones

funci´n dentro de otra. En este caso, la definici´n de funci´n interior no se
     o                                            o        o
ejecuta hasta que no se llama a la funci´n exterior.
                                        o
Las llamadas a funciones son como un desv´ en el flujo de ejecuci´n. En lugar
                                              ıo                      o
de ir a la siguiente sentencia, el flujo salta hasta la primera l´
                                                                ınea de la funci´n
                                                                                o
a 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                             o
Esto suena bastante sencillo... hasta que se acuerda de que una funci´n puede
                                                                     o
llamar a otra. Mientras estamos en medio de una funci´n, podr´
                                                         o       ıamos vernos
obligados a abandonarla e ir a ejecutar sentencias en otra funci´n m´s. Pero
                                                                 o    a
mientras estamos en esta nueva funci´n, ¡podr´
                                       o        ıamos salirnos y ejecutar otra
funci´n m´s!
     o    a
Afortunadamente, a Python se le da bien tomar nota de d´nde est´, de manera
                                                          o       a
que cada vez que se completa una funci´n, el programa retoma el punto en donde
                                      o
lo dej´ en la funci´n que hizo la llamada. Cuando llega al final del programa,
      o            o
termina.
¿Cu´l es la moraleja de toda esta historia? Cuando est´ leyendo un programa,
    a                                                    e
no lo lea desde la parte superior a la inferior. En lugar de eso, siga el flujo de
ejecuci´n.
       o



3.9.     Par´metros y argumentos
            a
Algunas de las funciones internas que hemos usado precisan de argumentos, los
valores que controlan c´mo la funci´n lleva a cabo su tarea. Por ejemplo, si
                        o           o
desea encontrar el seno de un n´mero, tiene que indicar de qu´ n´mero se trata.
                               u                             e u
As´ pues, sin toma como argumento un valor num´rico.
   ı                                               e
Algunas funciones toman m´s de un argumento, como pow, que toma dos argu-
                           a
mentos: la base y el exponente. Dentro de la funci´n, los valores que se le han
                                                  o
pasado se asignan a variables llamadas par´metros.
                                          a
He aqu´ un ejemplo de una funci´n definida por el usuario, que toma un par´me-
      ı                        o                                         a
tro:
def imprimeDoble(paso):
    print paso, paso
Esta funci´n toma un unico argumento y se lo asigna a un par´metro llamado
           o           ´                                       a
paso. El valor del par´metro (en este punto todav´ no tenemos ni idea de cu´l
                      a                           ıa                        a
ser´) se imprime dos veces, seguido por un car´cter de nueva l´
   a                                           a              ınea. El nombre
paso se eligi´ para sugerir que el nombre que le d´ a un par´metro depende de
             o                                    e         a
usted, 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                                               29

La funci´n imprimeDoble sirve con cualquier tipo (de dato) que se pueda im-
        o
primir:
  >>> imprimeDoble(’Jam´n’)
                       o
  Jam´n Jam´n
     o     o
  >>> imprimeDoble(5)
  5 5
  >>> imprimeDoble(3.14159)
  3.14159 3.14159
En la primera llamada a la funci´n, el argumento es una cadena; en la segunda
                                  o
es un entero, y en la tercera es un n´mero de coma flotante.
                                     u
Las mismas reglas de composici´n que se aplican a las funciones internas se
                                o
aplican 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.0
Como de costumbre, se eval´a la expresi´n antes de ejecutar la funci´n, de modo
                          u            o                            o
que imprimeDoble devuelve Jam´nJam´nJam´nJam´n Jam´nJam´nJam´nJam´n
                                 o     o     o     o      o      o      o    o
en lugar de ’Jam´n’*4’Jam´n’*4.
                o         o
Asimismo 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 variable
que pasamos como argumento (latoya) no tiene nada que ver con el nombre del
par´metro (paso). No importa c´mo se llamaba el valor en su lugar original (el
   a                            o
lugar desde donde se invoc´); aqu´ en imprimeDoble llamamos a todo el mundo
                          o      ı
paso.


3.10.      Las variables y los par´metros son locales
                                  a
Cuando crea una variable dentro de una funci´n, s´lo existe dentro de dicha
                                                 o   o
funci´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                                                                 Funciones

toma 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-
                                                             a
mirla, obtendr´
              ıamos un error:
  >>> print cat
  NameError: cat
Los par´metros tambi´n son locales. Por ejemplo, una vez fuera de la funci´n
       a             e                                                     o
imprimeDoble, no existe nada llamado paso. Si trata de usarla, Python se que-
jar´.
   a



3.11.      Diagramas de pila
Para 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 diagramas
de pila muestran el valor de cada variable, pero tambi´n muestran la funci´n a
                                                      e                   o
la 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. Por
e          a                                               o
ejemplo, 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
                                              o
por catDoble y a catDoble lo invoc´ main , que es un nombre especial de la
                                   o
3.12 Funciones con resultado                                                   31

funci´n m´s alta. Cuando crea una variable fuera de cualquier funci´n, pertenece
     o   a                                                         o
a main .
En cada caso, el par´metro se refiere al mismo valor que el argumento corres-
                    a
pondiente. 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
                                                  o
de la funci´n y el nombre de la funci´n que la llam´, y el nombre de la funci´n
           o                         o              o                        o
que llam´ a ´sa, y as´ hasta main .
         o e         ı
Por ejemplo, si intentamos acceder a cat desde imprimeDoble, provocaremos
un 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 cat
NameError: cat

Esta lista de funciones de llama traceback (traza inversa). Le dice a usted en
qu´ archivo de programa sucedi´ el error, y en qu´ l´
   e                             o                 e ınea, y qu´ funciones se
                                                                e
ejecutaban en ese momento. Tambi´n muestra la l´
                                    e             ınea de c´digo que caus´ el
                                                            o              o
error.
 ıjese en la similaridad entre la traza inversa y el diagrama de pila. No es una
F´
coincidencia.



3.12.      Funciones con resultado
Seguramente ha notado ya que algunas de las funciones que estamos usando,
igual que las funciones matem´ticas, devuelven un resultado. Otras funciones,
                             a
como nueva linea, llevan a cabo una acci´n pero no devuelven un valor. Ello
                                         o
suscita 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.      Glosario
llamada 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.
        e

argumento: 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                                 o

valor 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                                                o

conversi´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.
                     o

m´dulo: Fichero que contiene una colecci´n de funciones y clases relacionadas.
 o                                      o

notaci´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.
             o

funci´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.
                  a

flujo de ejecuci´n: Orden en el que se ejecutan las sentencias durante la eje-
                o
     cuci´n de un programa.
         o
3.13 Glosario                                                             33

par´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                                   o

diagrama 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
Todo Sobre El Lenguaje Python
Cap´
   ıtulo 4

Condicionales y
recursividad

4.1.     El operador m´dulo
                      o
El operador m´dulo funciona con enteros (y expresiones enteras), y devuelve
                 o
el resto de dividir el primer operando entre el segundo. En Python, el operador
de m´dulo es el signo de tanto por ciento ( %). La sintaxis es la misma de los
      o
otros operadores:
>>>   cociente = 7 / 3
>>>   print cociente
2
>>>   resto = 7 % 3
>>>   print resto
1
As´ 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
                    u
divisible entre y.

Tambi´n puede usar el operador m´dulo para extraer el d´
      e                           o                      ıgito m´s a la derecha
                                                                 a
de un n´mero. Por ejemplo, x % 10 devuelve el d´
        u                                       ıgito m´s a la derecha de x (en
                                                       a
base 10). De forma similar, x % 100 devuelve los dos ultimos d´
                                                     ´         ıgitos.
36                                               Condicionales y recursividad

4.2.       Expresiones booleanas
Una expresi´n booleana es una expresi´n que es cierta o falsa. En Python,
              o                            o
una expresi´n que es cierta tiene el valor 1, y una expresi´n que es falsa tiene
            o                                              o
el valor 0.
El operador == compara dos valores y entrega una expresi´n booleana:
                                                        o
>>> 5 == 5
1
>>> 5 == 6
0
En la primera sentencia, los dos operandos son iguales, as´ que la expresi´n se
                                                          ı               o
eval´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 y
Aunque probablemente estas operaciones le resulten familiares, los s´ımbolos en
Python son diferentes de los matem´ticos. Un error habitual es utilizar un signo
                                   a
igual sencillo (=) en lugar del doble (==). Recuerde que = es un operador de
asignaci´n y == es un operador de comparaci´n. Adem´s, no existen =< ni =>.
        o                                    o        a



4.3.       Operadores l´gicos
                       o
Hay tres operadores l´gicos: and, or, y not. La sem´ntica (significado) de
                        o                                a
estos operadores es similar a sus significados en ingl´s. Por ejemplo, x >0 and
                                                     e
x <10 es verdadero s´lo si x es mayor que 0 y menor que 10.
                     o
n %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.
                    u
Finalmente, 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           ıan
expresiones booleanas, pero Python no es muy estricto. Cualqueir n´mero que
                                                                  u
no sea cero se interpreta como “verdadero”.
4.4 Ejecuci´n condicional
           o                                                                  37

>>>    x = 5
>>>    x and 1
1
>>>    y = 0
>>>    y and 1
0
En general, este tipo de cosas no se considera buen estilo. Si quiere comparar
un valor con cero, deber´ hacerlo expl´
                        ıa            ıcitamente.


4.4.     Ejecuci´n condicional
                o
Para 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      a
sencilla 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                                        o
la sentencia indentada se ejecuta. Si la condici´n no es verdadera, no pasa nada.
                                                o
Como otras sentencias compuestas, if consta de una cabecera y un bloque de
sentencias:
CABECERA:
  PRIMERA SENTENCIA
  ...
  ULITMA SENTENCIA
La 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. La
primera sentencia no indentada marca el fin del bloque. Un bloque de sentencias
dentro 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 de
una 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
                                                                       o
todav´ no ha escrito). En tales casos, puede usted utilizar la sentencia pass,
      ıa
que no hace nada.


4.5.     Ejecuci´n alternativa
                o
Una segunda forma de la sentencia if es la ejecuci´n alternativa, en la que hay
                                                  o
dos posibilidades, y la condici´n determina cu´l de ellas se ejecuta. La sintaxis
                               o              a
38                                             Condicionales y recursividad

tiene 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
                                                                  o
ejecuta el segundo lote de sentencias. Puesto que la condici´n debe ser verdadera
                                                            o
o falsa, se ejecutar´ exactamente una de las alternativas. Llamamos ramas a
                    a
las posibilidades porque son ramas del flujo de ejecuci´n.
                                                        o
Como un aparte, si piensa que querr´ comprobar con frecuencia la paridad de
                                    a
n´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-
                     o
piado para cada n´mero entero que usted le pase. Llame a esta funci´n de la
                  u                                                o
manera siguiente:
>>> imprimeParidad(17)
>>> imprimeParidad(y+1)



4.6.     Condiciones encadenadas
A veces hay m´s de dos posibilidades y necesitamos m´s de dos ramas. Una
              a                                      a
forma 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              a
hay l´
     ımite al n´mero de sentencias elif, pero s´lo se permite una sentencia
               u                                  o
else (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 comprueba
la 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   o
la primera rama verdadera.

     Como ejercicio, envuelva estos ejemplos en funciones llamadas
     compara(x, y) y resuelve(eleccion).


4.7.     Condiciones anidadas
Una 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", y
La condici´n externa que contiene dos ramas. La primera rama contiene una
           o
sentencia simple de salida. La segunda rama contiene otra sentencia if, que
tiene dos ramas en s´ misma. Estas dos ramas son ambas sentencias de salida
                    ı
de datos, aunque podr´ ser igualmente sentencias condicionales.
                       ıan
Aunque la indentaci´n de las sentencias hace la estructura evidente, las condi-
                    o
ciones anidadas en seguida se vuelven dif´
                                         ıciles de leer. En general es una buena
idea evitarlas cuando pueda.
Los operadores l´gicos suelen facilitar un modo de simplificar las sentencias
                 o
condicionales anidadas. Por ejemplo, podemos reescribir el c´digo siguiente con
                                                            o
un s´lo condicional:
    o
if 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,
                    o
as´ que podemos usar el operador and:
  ı
40                                             Condicionales y recursividad

if 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 proporciona
una sintaxis alternativa similar a la notaci´n matem´tica:
                                            o       a

if 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                                 o
ta y que el condicional anidado.



4.8.     La sentencia return
La sentencia return le permite terminar la ejecuci´n de una funci´n antes de
                                                   o              o
alcanzar 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", result
La funci´n imprimeLogaritmo toma un par´metro llamado x. Lo primero que
         o                                 a
hace es comprobar si x es menor o igual que cero, en cuyo caso muestra un
mensaje de error y luego usa return para salir de la funci´n. El flujo de la
                                                              o
ejecuci´n vuelve inmediatamente al llamante y no se ejecutan las l´
       o                                                          ıneas restantes
de la funci´n.
           o

Recuerde que para usar una funci´n del m´dulo math tiene que importarlo.
                                o       o



4.9.     Recursividad
Ya mencionamos que es legal que una funci´n llame a otra, y de ello hemos visto
                                          o
ya varios ejemplos. Olvidamos mencionar que tambi´n es legal que una funci´n
                                                   e                         o
se llame a s´ misma. Puede no resultar evidente por qu´ es bueno esto, pero
             ı                                           e
viene a resultar una de las cosas m´s interesantes y curiosas que puede hacer
                                   a
un 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                                           a
tro es cero, muestra la palabra “Despegando!”. En otro caso, muestra n y luego
llama a la funci´n llamada cuenta atras (ella misma) pas´ndole como argu-
                o                                           a
mento 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
          o
como 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
                  a
salida completa presenta el siguiente aspecto:
3
2
1
Despegando!
Como segundo ejemplo, consideremos de nuevo las funciones nuevaLinea and
tresLineas.
  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                                     ıneas
nuevas 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), que
si rescata su ´lgebra ver´ que es n.
              a          a


El 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-
           vas

El la Secci´n 3.11 utilizamos un diagrama de pila para representar el estado de
           o
un programa durante la llamada de una funci´n. El mismo tipo de diagrama
                                               o
puede hacer m´s f´cil interpretar una funci´n recursiva.
               a a                         o


Cada vez que se llama a una funci´n, Python crea un nuevo marco para la
                                    o
funci´n, que contiene sus variables locales y par´metros. En el caso de una
     o                                           a
funci´n recursiva, puede haber m´s de un marco en la pila al mismo tiempo.
     o                          a


La 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        0


Como es habitual, en lo alto de la pila est´ el marco de main . Est´ vac´
                                           a                       a    ıa
porque no hemos ninguna variable sobre main ni le hemos pasado ning´n  u
par´metro.
   a
Los cuatro marcos de cuenta atras tienen valores diferentes para el par´metro
                                                                       a
n. El fondo de la pila, donde n=0, se llama caso base. No hace una llamada
recursiva, 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.
               a



4.11.      Recursividad infinita
Si una recursi´n no alcanza nunca el caso base, seguir´ haciendo llamadas re-
              o                                       a
cursivas para siempre y nunca terminar´. Esta circunstancia se conoce como
                                        a
recursividad infinita, y generalmente no se la considera una buena idea. Este
es un programa m´ ınimo con recursividad infinita:

def recurre():
  recurre()

El la mayor´ de los entornos de programaci´n, un programa con recursividad
            ıa                               o
infinita no se ejecutar´ realmente para siempre. Python informar´ de un mensaje
                      a                                        a
de 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 recurse
RuntimeError: Maximum recursion depth exceeded
44                                            Condicionales y recursividad

Esta 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              e


4.12.      Entrada por teclado
Los programas que hemos escrito hasta ahora son un poco maleducados en el
sentido de que no aceptan entradas de datos del usuario. Simplemente hacen lo
mismo 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                                                             o
grama se detiene y espera a que el usuario escriba algo. Cuando el usuario pulsa
la tecla Return o Enter, el programa se reanuda y raw input devuelve lo que
el usuario escribi´ como tipo string:
                  o
   >>> entrada = raw_input ()
   A qu´ est´s esperando?
         e     a
   >>> print entrada
   A qu´ est´s esperando?
         e     a
Antes de llamar a raw input es conveniente mostrar un mensaje que le pida al
usuario el dato solicitado. Este mensaje se llama indicador (prompt en ingl´s).
                                                                            e
Puede 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         e
Si 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                    a
asignar´ a velocidad. Por desgracia, si el usuario escribe algo que no sea un
         a
d´ı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 syntax
Para evitar este tipo de error, generalmente es buena idea usar raw input para
obtener una cadena y usar entonces las funciones de conversi´n para convertir
                                                               o
a otros tipos.
4.13 Glosario                                                               45

4.13.      Glosario
operador 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                        o

operador 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.
                                        o

condici´n: La expresi´n booleana de una sentencia condicional que determina
       o             o
    qu´ rama se ejecutar´.
       e                a

sentencia 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.
               ıa

bloque: 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.
                                o

indicador: indicador visual que invita al usuario a introducir datos.
Todo Sobre El Lenguaje Python
Cap´
   ıtulo 5

Funciones productivas

5.1.     Valores de retorno
Algunas de las funciones internas que hemos usado, como las funciones math o
funciones matem´ticas, han producido resultados. Llamar a la funci´n genera un
                a                                                 o
nuevo valor, que normalmente asignamos a una variable pasa usar como parte
de 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 un
valor.
En este cap´
           ıtulo escribiremos funciones que devuelvan valores, que llamaremos
funciones productivas, a falta de un nombre mejor. El primer ejemplo es
area, que devuelve el ´rea de un c´
                       a          ırculo con un radio dado:
    import math

     def area(radio):
       temporal = math.pi * radio**2
       return temporal
Ya hemos visto antes la sentencia return, pero en una funci´n productiva la
                                                             o
sentencia 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                          o
retorno”. 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**2
Por otra parte, las variables temporales como temporal suelen hacer m´s
                                                                     a
f´cil el depurado.
 a

A 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 x
Puesto que estas sentencias return est´n en una condici´n alternativa, s´lo se
                                        a                  o               o
ejecutar´ una de ellas. En cuanto se ejecuta una de ellas, la funci´n termina sin
        a                                                          o
ejecutar ninguna de las sentencias siguientes.

El c´digo que aparece despu´s de una sentencia return o en cualquier otro lugar
    o                       e
donde el flujo de ejecuci´n no pueda llegar, recibe el nombre de c´digo muerto.
                        o                                        o

En una funci´n productiva es una buena idea asegurarse de que cualquier posible
             o
recorrido del programa alcanza una sentencia return. Por ejemplo:
     def valorAbsoluto(x):
       if x < 0:
         return -x
       elif x > 0:
         return x
Este programa no es correcto porque si resulta que x vale 0, entonces no se cum-
ple ninguna de ambas condiciones y la funci´n termina sin alcanzar la setencia
                                             o
return. 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 programas
Llegados a este punto, tendr´ que poder mirar a funciones Python completas
                             ıa
y adivinar qu´ hacen. Tambi´n, si ha hecho los ejercicios, habr´ escrito algu-
              e               e                                a
nas funcioncillas. Tal como vaya escribiendo funciones mayores puede empezar
5.2 Desarrollo de programas                                                       49

a experimentar m´s dificultades, especialmente con los errores en tiempo de
                   a
ejecuci´n y los sem´nticos.
       o            a

Para lidiar con programas de complejidad creciente, vamos a sugerirle una t´cni-
                                                                           e
ca 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               o
peque˜as porciones de c´digo en cada vez.
      n                 o

Por 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
                                                                      a
escribir 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         o
Python. En otras palabras, ¿cu´les son las entradas (par´metros) y cu´l es la
                              a                         a            a
salida (valor de retorno)?

En este caso, los dos puntos son los par´metros, que podemos representar usando
                                        a
cuatro par´metros. El valor de retorno es la distancia, que es un valor en coma
           a
flotante.

Ya podemos escribir un bosquejo de la funci´n:
                                           o
     def distancia(x1, y1, x2, y2):
       return 0.0
Obviamente, la funci´n no calcula distancias; siempre devuelve cero. Pero es
                     o
sint´cticamente correcta y se ejecutar´, lo que significa que podemos probarla
    a                                 a
antes de complicarla m´s.
                       a

Para 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 la
distancia vertical sea igual a 4; de esa manera el resultado es 5 (la hipotenusa del
tri´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        a
ta, as´ que podemos empezar a a˜adir l´
      ı                        n      ıneas de c´digo. Despu´s de cada cambio
                                                o            e
50                                                      Funciones productivas

incremental, comprobamos de nuevo la funci´n. Si en un momento dado apare-
                                          o
ce un error, sabremos d´nde est´ exactamente: en la ultima l´
                       o       a                    ´       ınea que hayamos
a˜adido.
 n

El siguiente paso en el c´lculo es encontrar las diferencias entre x2 −x1 y y2 −y1 .
                         a
Almacenaremos dichos valores en variables temporales llamadas dx y dy y las
imprimiremos.
     def distancia(x1, y1, x2, y2):
       dx = x2 - x1
       dy = y2 - y1
       print "dx es", dx
       print "dy es", dy
       return 0.0
Si la funci´n funciona, valga la redundancia, las salidas deber´ ser 3 y 4. Si
           o                                                    ıan
es as´ sabemos que la funci´n recibe correctamente los par´metros y realiza
     ı,                      o                                a
correctamente 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.0
F´ı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).
          ıa

Por ultimo, si hemos importado el m´dulo math, podemos usar la funci´n sqrt
    ´                                o                              o
para 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 resultado
Si esto funciona correctamente, ha terminado. Si no, podr´ ser que quisiera
                                                           ıa
usted imprimir el valor de resultado antes de la sentencia return.
5.3 Composici´n
             o                                                                 51

Al principio, deber´ a˜adir solamente una o dos l´
                   ıa n                              ıneas de c´digo cada vez.
                                                                o
Conforme vaya ganando experiencia, puede que se encuentre escribiendo y de-
purando trozos mayores. Sin embargo, el proceso de desarrollo incremental puede
ahorrarle 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
                  o
Como seguramente a estas alturas ya supondr´, se puede llamar a una funci´n
                                              a                          o
desde dentro de otra. Esta habilidad se llama composici´n .
                                                       o
Como ejemplo, escribiremos una funci´n que tome dos puntos, el centro del
                                       o
c´
 ı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
                                        a
que el punto del per´ ımetro lo est´ en xp e yp. El primer paso es hallar el radio
                                   a
del c´
     ırculo, que es la distancia entre los dos puntos. Afortunadamente hay una
funci´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 resultado
Envolviendo 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 productivas

Hemos llamado a esta funci´n area2 para distinguirla de la funci´n area definida
                          o                                     o
anteriormente. S´lo puede haber una unica funci´n con un determinado nombre
                o                   ´          o
dentro de un m´dulo.
               o
Las 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                                  a
conciso 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´          e


5.4.     Funciones booleanas
Las funciones pueden devolver valores booleanos, lo que a menudo es conveniente
para 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 false
La funci´n lleva por nombre esDivisible. Es habitual dar a las funciones boo-
         o
leanas nombres que suenan como preguntas s´ı/no. Devuelve 1 ´ 0 para indicar
                                                             o
si 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           a
la sentencia condicional que hay despu´s del if es en s´ misma una expresi´n
                                      e                ı                   o
booleana. Podemos devolverla directamente, evitando a la vez la sentencia if:
      def esDivisible(x, y):
        return x % y == 0
La siguiente sesi´n muestra a la nueva funci´n en acci´n:
                 o                          o         o
>>>    esDivisible(6, 4)
0
>>>    esDivisible(6, 3)
1
El uso m´s com´n para las funciones booleanas es dentro de sentencias condi-
          a   u
cionales:
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
           a
Hasta ahora, usted ha aprendido solamente un peque˜o subconjunto de Python,
                                                      n
pero puede que le interese saber que ese subconjunto es ya un lenguaje de
programaci´n completo; con esto queremos decir que cualquier cosa que pueda
            o
computarse se puede expresar en este lenguaje. Cualquier programa que se haya
escrito alguna vez puede reescribirse utilizando unicamente las caracter´
                                                  ´                           ısticas
del 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, pero
o                                                          o
eso es todo).
Probar tal afirmaci´n es un ejercicio nada trivial, completado por primera vez
                    o
por Alan Turing, uno de los primeros cient´
                                          ıficos inform´ticos (algunos argumen-
                                                      a
tar´n que era un matem´tico, pero muchos de los cient´
   a                     a                            ıficos inform´ticos pione-
                                                                   a
ros comenzaron como matem´ticos). En correspondencia, se la conoce como la
                              a
tesis de Turing. Si estudia un curso de Teor´ de la Computaci´n, tendr´ opor-
                                            ıa                 o        a
tunidad de ver la prueba.
Para darle una idea de lo que puede hacer con las herramientas que ha aprendido
hasta ahora, evaluaremos una serie de funciones matem´ticas que se definen
                                                           a
recursivamente. Una definici´n recursiva es semejante a una definici´n circular,
                             o                                        o
en el sentido de que la definici´n contiene una referencia a lo que se define. Una
                               o
definici´n verdaderamente circular no es muy util:
        o                                      ´

frangoso: adjetivo que describe algo que es frangoso

Si usted viera esa definici´n en el diccionario, se quedar´ confuso. Por otra parte,
                          o                              ıa
si ha buscado la definici´n de la funci´n matem´tica factorial, habr´ visto algo
                         o               o          a                 a
sejemante 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
             o
otro 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                                       a
un programa de Python para evaluarlo. El primer paso es decidir cu´les son los
                                                                   a
par´metros para esta funci´n. Con poco esfuerzo llegar´ a la conclusi´n de que
   a                      o                           a              o
factorial 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 1
En 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 resultado
El flujo de ejecuci´n de este programa es similar al de cuenta atras de la
                    o
Secci´n 4.9. Si llamamos a factorial con el valor 3:
     o
Puesto que 3 no es 0, tomamos la segunda rama y calculamos el factorial de
n-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 convierte
en el valor de retorno de la llamada a la funci´n que comenz´ todo el proceso.
                                                o                 o

He 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        0


Los valores de retorno se muestran seg´n se pasan hacia la parte superior de
                                        u
la pila. En cada marco, el valor de retorno es el valor de resultado, que es el
producto 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 fe
Seguir el flujo de ejecuci´n es una de las maneras de leer programas; pero puede
                         o
volverse r´pidamente una tarea laber´
          a                            ınitca. La alternativa es lo que llamamos
el “acto de fe”. Cuando llegamos a una funci´n, en lugar de seguir el flujo de
                                                o
ejecuci´n, damos por sentado que la funci´n trabaja correctamente y devuelve
       o                                    o
el 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 productivas

dichas funciones. Simplemente da por sentado que funcionan porque los que
escribieron 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                             o
que determina si un n´mero es divisible por otro. Una vez que nos hayamos
                      u
convencido de que dicha funci´n es correcta, comprobando y examinando el
                               o
c´digo, podremos usar la funci´n sin tener siquiera que volver a mirar el c´digo
 o                            o                                            o
otra 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        ıa
la llamada recursiva funciona (es decir, devuelve el resultado correcto) y luego
preguntarse: “suponiendo que puedo hallar el factorial de n − 1, ¿puedo hallar
el factorial de n?” En este caso, est´ claro que s´ puede, multiplic´ndolo por n.
                                     a            ı                 a
Por supuesto, es un tanto extra˜o dar por supuesto que la funci´n est´ bien
                               n                                  o      a
cuando ni siquiera ha acabado de escribirla, pero precisamente por eso se llama
acto de fe.


5.7.     Un ejemplo m´s
                     a
En 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                            ıamos
habernos 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    a
mendamos que utilice la versi´n m´s expl´
                              o     a     ıcita mientras se halle desarrollando
c´digo. Cuando lo tenga funcionando, lo podr´ acortar, si se siente inspirado.
 o                                            a
Despu´s de factorial, el ejemplo m´s com´n de una funci´n matem´tica re-
      e                            a      u               o          a
cursivamente 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
       n
fe, si da por supuesto que las dos llamadas recursivas funcionan correctamente,
entonces estar´ claro que obtiene el resultado correcto al sumarlas juntas.
               a


5.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 exceeded
Tiene todo el aspecto de una recursi´n infinita. Pero, ¿c´mo ha podido ocurrir?
                                    o                   o
Hay una condici´n de salida o caso base: cuando n == 0. El problema es que el
                o
valor de n yerra el caso base.
En la primera llamada recursiva, el valor de n es 0.5. En la siguiente vez su valor
es -0.5. A partir de ah´ se vuelve m´s y m´s peque˜o, pero nunca ser´ 0.
                       ı,            a      a        n                   a
Tenemos dos opciones. Podemos intentar generalizar la funci´n factorial para
                                                            o
que trabaje con n´meros de coma flotante, o podemos hacer que factorial
                   u
compruebe el tipo de su par´metro. La primera opci´n se llama funci´n gamma,
                            a                         o              o
y 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                                   a
de un valor entero conocido (por ejemplo 1). Ya que estamos en ello, podemos
asegurarnos 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 productivas

Ahora tenemos tres condiciones de salida o casos base. El primero filtra los
n´meros no enteros. El segundo evita los enteros negativos. En ambos casos, se
 u
muestra un mensaje de error y se devuelve un valor especial, -1, para indicar a
quien 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.
     -1
Si pasamos ambas comprobaciones, entonces sabemos que n es un entero positivo
y podemos probar que la recursi´n termina.
                               o

Este programa muestra un patr´n que se llama a veces guardi´n. Las primeras
                                o                              a
dos condicionales act´an como guardianes, protegiendo al c´digo que sigue de
                      u                                      o
los valores que pudieran causar errores. Los guardianes hacen posible demostrar
la correcci´n del c´digo.
           o       o



5.9.     Glosario
funci´n productiva: Funci´n que devuelve un valor de retorno.
     o                   o

valor de retorno: El valor obtenido como resultado de una llamada a una
     funci´n.
          o

variable temporal: Variable utilizada para almacenar un valor intermedio en
     un c´lculo complejo.
         a

c´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                                                           59

guardi´n: Una condici´n que comprueba y maneja circunstancias que pudieran
      a              o
    provocar un error.
Todo Sobre El Lenguaje Python
Cap´
   ıtulo 6

Iteraci´n
       o

6.1.      Asignaci´n m´ ltiple
                  o   u
Es posible que haya descubierto que es posible hacer m´s de una asignaci´n a
                                                       a                    o
una misma variable. El efecto de la nueva asignaci´n es redirigir la variable de
                                                  o
manera que deje de remitir al valor antiguo y empieze a remitir al valor nuevo.
  bruno   = 5
  print   bruno,
  bruno   = 7
  print   bruno
La salida del programa es 5 7, ya que la primera vez que imprimimos bruno
su valor es 5, y la segunda vez su valor es 7. La coma al final de la primera
sentencia print impide que se imprima una nueva l´ ınea en ese punto, por eso
ambos 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
                                            7


Cuando hay asignaciones m´ltiples a una variable, es especialmente importante
                            u
distinguir entre una sentencia de asignaci´n y una sentencia de igualdad. Puesto
                                          o
que Python usa el s´  ımbolo = para la asignaci´n, es tentador interpretar una
                                                o
sentencia como a = b como sentencia de igualdad. Pero no lo es.
62                                                                     Iteraci´n
                                                                              o

Para empezar, la igualdad es commutativa, y la asignaci´n no lo es. Por ejemplo
                                                       o
en matem´ticas si a = 7 entonces 7 = a. Pero en Python la sentencia a = 7 es
           a
legal, y 7 = a no lo es.
Y lo que es m´s, en matem´ticas, una sentencia de igualdad es verdadera todo
              a            a
el tiempo. Si a = b ahora, entonces a siempre ser´ igual a b. En Python, una
                                                 a
sentencia de asignaci´n puede hacer que dos variables sean iguales, pero no
                     o
tienen por qu´ quedarse as´
              e           ı.
  a = 5
  b = a      # a y b son ahora iguales
  a = 3      # a y b ya no son iguales
La tercera l´
            ınea cambia el valor de a pero no cambia el valor de b, y por lo tanto
ya dejan de ser iguales. En algunos lenguajes de programaci´n, se usa para la
                                                               o
asignaci´n un s´
        o       ımbolo distinto, como <- o como :=, para evitar la confusi´n.o
Aunque 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 del
programa, podr´ suceder que el c´digo sea dif´ de leer y mantener.
                ıa                o          ıcil


6.2.      La sentencia while
Una de las tareas para las que los computadores se usan con frecuencia es la
automatizaci´n de tareas repetitivas. Repetir tareas similares o id´nticas es algo
            o                                                      e
que los computadores hacen bien y las personas no hacen tan bien.
Hemos visto dos programas, nLineas y cuenta atras, que usan la recursividad
para llevar a cabo la repetici´n, que tambi´n se llama iteraci´n. Por ser la
                              o              e                  o
iteraci´n tan habitual, Python proporciona como lenguaje varias caracter´
       o                                                                ısticas
que la hacen m´s f´cil. La primera caracter´
                a a                         ıstica que vamos a considerar es la
sentencia 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.
                                                o
Casi podr´ leer una sentencia while como si fuera ingl´s (castellano “mien-
          ıa                                              e
tras”). Quiere decir que “Mientras n sea mayor que cero, contin´a mostrando el
                                                               u
valor de n y despu´s rest´ndole 1 al valor de n. Cuando llegues a cero, muestra
                   e      a
la palabra “Despegando!”.
6.2 La sentencia while                                                           63

M´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
             a
el 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                      o
sentencias 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,
                                                      a
llegado el momento, la condici´n sea falsa y el bucle termine. En caso contrario,
                              o
el bucle se repetir´ para siempre, que es lo que se llama bucle infinito. Una
                   a
infinita fuente de diversi´n para los cient´
                         o                ıficos inform´ticos es la observaci´n de
                                                       a                    o
que las instrucciones del champ´ “lavar, aclarar, repetir”, son un bucle infinito.
                                u
En el caso de cuenta atras, podemos probar que el bucle terminar´ porque
                                                                     a
sabemos que el valor de n es finito, y podemos ver que el valor de n disminuye
cada vez que se atraviesa el bucle (cada iteraci´n), de manera que ea la larga
                                                o
tenemos 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+1
La condici´n de este bucle es n != 1, de manera que el bucle continuar´ hasta
          o                                                           a
que n sea 1, que har´ que la condici´n sea falsa.
                    a               o
En cada iteraci´n, el programa muestra como salida el valor de n y luego com-
                 o
prueba 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 argumento
pasado 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 de
que n alcance alguna vez el valor 1, o de que el programa vaya a terminar. Para
algunos valores particulares de n, podemos probar la terminaci´n. Por ejemplo,
                                                               o
64                                                                     Iteraci´n
                                                                              o

si el valor de inicio es una potencia de dos, entonces el valor de n ser´ par cada
                                                                        a
vez que se pasa a trav´s del bucle, hasta que lleguemos a 1. El ejemplo anterior
                         e
acaba 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,
                                a
nadie 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.
                  o



6.3.     Tablas
Una 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
            ıa
matem´ticas. Para facilitarlo, los libros de matem´ticas conten´ largas tablas
       a                                           a             ına
donde aparec´ los valores de estas funciones. Confeccionar estas tablas era
              ıan
una tarea lenta y pesada, y el resultado estaba lleno de erratas.

Cuando los computadores aparecieron en escena, una de las primeras reacciones
fue “¡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              e
computadores y las calculadoras cient´ıficas se hicieron tan ubicuas que las tablas
resultaron obsoletas.

Bueno, casi. Resulta que para ciertas operaciones, los computadores usan tablas
para obtener un valor aproximado, y luego ejecutan c´lculos para mejorar la
                                                         a
aproximaci´n. En algunos casos, ha habido errores en las tablas subyacentes; el
            o
m´s famoso estaba en la tabla que el Pentium de Intel usaba para llevar a cabo
  a
la divisi´n de coma flotante.
         o

Aunque una tabla logar´ıtmica ya no es tan util como lo fuera anta˜o, todav´
                                           ´                      n        ıa
constituye un buen ejemplo de iteraci´n. El siguiente programa muestra una
                                     o
secuencia de valores en la columna izquierda y sus logaritmos en la columna
derecha:
  x = 1.0
  while x < 10.0:
    print x, ’t’, math.log(x)
    x = x + 1.0
El t representa un car´cter de tabulaci´n.
                       a                o
6.3 Tablas                                                                    65

Tal como se van mostrando en la pantalla caracteres y cadenas, un se˜alador
                                                                         n
invisible llamado cursor lleva la cuenta de d´nde ir´ el pr´ximo car´cter. Tras
                                             o      a      o         a
una 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                o
alcance 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.19722457734
Si estos valores le parecen raros, recuerde que la funci´n log usa como base e.
                                                        o
Debido a que las potencias de dos son muy importantes en las ciencias de la
computaci´n, generalmente querremos hallar los logaritmos en relaci´n con la
           o                                                          o
base dos. Para llevarlo a cabo, podemos usar la siguiente f´rmula:
                                                            o

                                            loge x
                                 log2 x =                                   (6.1)
                                            loge 2

Cambiar 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.16992500144
Podemos ver que 1, 2, 4 y 8 son potencias de dos, porque sus logaritomos de
base 2 son n´meros enteros. Si quisi´ramos encontrar los logaritmos de otras
             u                      e
potencias 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.0
Ahora, en lugar de a˜adir algo a x cada vez que atravesamos el bucle, que
                      n
devuelve una secuencia aritm´tica, multiplicamos x por algo, devolviendo una
                              e
secuencia 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.0
Debido a que usamos caracteres de tabulaci´n entre las columnas, la posici´n de
                                          o                               o
la 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
      a
escape. Las secuencias de escape se usan para representar caracteres invisibles
como tabulaciones y retornos de carro. La secuencia n representa un retorno
de carro.
Una sentencia de escape puede aparecer en cualquier lugar de una cadena; en el
ejemplo, 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 dimensiones
Una tabla de dos dimensiones es una tabla en la que Usted elige una fila y
una columna y lee el valor de la intersecci´n. Un buen ejemplo es una tabla de
                                           o
6.5 Encapsulado y generalizaci´n
                              o                                                67

multiplicar. Supongamos que desea imprimir una tabla de multiplicar para los
valores del 1 al 6.

Una buena manera de comenzar es escribir un bucle sencillo que imprima los
m´ltiplos de 2, todos en una l´
 u                            ınea.
  i = 1
  while i <= 6:
    print 2*i, ’t’,
    i = i + 1
  print
La primera l´
            ınea inicializa una variable lllamada i, que actuar´ como contador,
                                                               a
o variable de bucle. Conforme se ejecuta el bucle, el valor de i se incrementa
de 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
                                                                         e
completar el bucle, la segunda sentencia print crea una l´
                                                         ınea nueva.

La salida de este programa es:
  2        4       6        8        10       12
Hasta ahora, bien. El siguiente paso es encapsular y generalizar.



6.5.     Encapsulado y generalizaci´n
                                   o
Por “encapsulado” generalmente se entiende tomar una pieza de c´digo y envol-
                                                                o
verla en una funci´n, permiti´ndole obtener las ventajas de todo aquello para
                   o         e
lo que valen las funciones. Hemos visto dos ejemplos de encapsulado, cuando
escribimos imprimeParidad en la Secci´n 4.5 y esDivisible en la Secci´n 5.4.
                                      o                                o

Por “generalizaci´n” entendemos tomar algo espec´
                  o                                ıfico, como imprimir los m´lti-
                                                                             u
plos de 2, y hacerlo m´s general, como imprimir los m´ltiplos de cualquier entero.
                      a                               u

He aqu´ una funci´n que encapsula el bucle de la secci´n anterior y la generaliza
       ı         o                                    o
para 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
                                                                            o

Para encapsular, todo lo que hubimos de hacer fue a˜adir la primera l´
                                                     n                 ınea, que
declara el nombre de la funci´n y la lista de par´metros. Para generalizar, todo
                             o                   a
lo que tuvimos que hacer fue sustituir el valor 2 por el par´metro n.
                                                            a

Si llamamos a esta funci´n con el argumento 2, obtenemos la misma salida que
                        o
antes. Con el argumento 3, la salida es:
  3       6       9        12      15      18
y con argumento 4, la salida es
  4       8           12    16      20       24
A estas alturas es probable que haya adivinado c´mo vamos a imprimir una
                                                 o
tabla de multiplicaci´n: llamaremos a imprimeMultiplos repetidamente con
                     o
diferentes argumentos. De hecho, podemos a usar otro bucle:
  i = 1
  while i <= 6:
    imprimeMultiplos(i)
    i = i + 1
Observe hasta qu´ punto este bucle es similar al que hay en el interior de
                 e
imprimeMultiplos. Todo lo que hicimos fue sustituir la sentencia print por
una llamada a una funci´n.
                       o

La 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      36



6.6.     M´s encapsulaci´n
          a             o
Para dar m´s ejemplos de encapsulaci´n, tomaremos el c´digo del final de la
            a                         o               o
Secci´n 6.5 y lo envolveremos en una funci´n:
     o                                    o
  def imprimeTablaMult():
    i = 1
    while i <= 6:
      imprimeMultiplos(i)
      i = i + 1
El 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                                                          69

el int´rprete. Cuando conseguimos que funcionen, se extraen y se envuelven en
      e
una funci´n.
          o
Este plan de desarrollo es especialmente si, al comenzar a escribir, no sabe
c´mo dividir el programa en funciones. Este enfoque le permite dise˜arlo sobre
 o                                                                 n
la marcha.


6.7.     Variables locales
Quiz´ se est´ preguntando c´mo podemos usar la misma variable tanto en
    a        e               o
imprimeMultiplos como en imprimeTablaMult. ¿No habr´ problemas cuan-
                                                           a
do una de las funciones cambie los valores de la variable?
La respuesta es no, ya que la variable i en imprimeMultiplos y la variable i
in imprimeTablaMult no son la misma variable.
Las variables creadas dentro de una funci´n son locales. No puede acceder a una
                                         o
variable local fuera de su funci´n “hu´sped”. Eso significa que es posible tener
                                o     e
m´ltiples variables con el mismo nombre, siempre que no est´n en la misma
  u                                                            e
funci´n.
     o
El diagrama de pila de esta funci´n muestra claramente que las dos variables
                                 o
llamadas i no son la misma variable. Pueden referirse a diferentes valores, y
cambiar uno no afecta al otro.

              printMultTable              1
                                  i       2
                                          3


               printMultiples                             1
                                 n        3       i       2



El 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                            a
imprimeTablaMult llama a imprimeMultiplos con el valor actual de i como
argumento. Ese valor se asigna al par´metro n.
                                      a
Dentro 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
                                                          u
de i en imprimeTablaMult.
Es habitual y perfectamente legal tener diferentes variables locales con el mismo
nombre. En especial, los nombres i, j y k se suelen usar como variables de
70                                                                 Iteraci´n
                                                                          o

bucle. Si evita usarlas en una funci´n porque las utiliz´ en alg´n otro lugar,
                                    o                   o       u
probablemente consiga que el programa sea m´s dif´ de leer.
                                             a    ıcil



6.8.     M´s generalizaci´n
          a              o
Como otro ejemplo de generalizaci´n, imagine que desea un programa que im-
                                 o
prima una tabla de multiplicaci´n de cualquier tama˜o, y no s´lo la tabla de
                               o                   n         o
6x6. Podr´ a˜adir un par´metro a imprimeTablaMult:
         ıa n           a
  def imprimeTablaMult(mayor):
    i = 1
    while i <= mayor:
      imprimeMultiplos(i)
      i = i + 1
Hemos sustituido el valor 6 con el par´metro mayor. Si ahora se llama a
                                      a
imprimeTablaMult 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      42
lo que es correcto, excepto por el hecho de que seguramente queremos que la
tabla est´ cuadrada, con el mismo n´mero de filas que de columnas. Para hacer-
         e                         u
lo, a˜adimos otro par´metro a imprimeMultiplos, a fin de especificar cu´ntas
     n                a                                                 a
columnas tendr´ que tener la tabla.
                ıa

S´lo para fastidiar, llamaremos tambi´n a este par´metro mayor, para demostrar
 o                                    e           a
que diferentes funciones pueden tener par´metros con el mismo nombre (al igual
                                         a
que 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 + 1
N´tese que al a˜adir un nuevo par´metro, tuvimos que cambiar la primera l´
  o            n                  a                                      ınea
de la funci´n (el encabezado de la funci´n), y tuvimos tambi´n que cambiar el
           o                            o                   e
lugar donde se llama a la funci´n en imprimeTablaMult.
                               o
Seg´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       49
Cuando generaliza correctamente una funci´n, a menudo se encuentra con que
                                             o
el 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        e
manera que todas las entradas de la tabla aparecen dos veces. Puede ahorrar
tinta imprimiendo s´lo la mitad de la tabla. Para hacerlo, s´lo tiene que cambiar
                   o                                        o
una 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.
                                                    o



6.9.     Funciones
Hasta el momento hemos mencionado en alguna ocasi´n “todas las cosas para
                                                      o
las que sirven las funciones”. Puede que ya se est´ preguntando qu´ cosas son
                                                  e                 e
exactamente. 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.      Glosario
asignaci´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.
                     o

iteraci´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.
                                      o

bucle: Sentencia o grupo de sentencias que se ejecutan repetidamente hasta
     que se cumple una condici´n de terminaci´n.
                              o              o

bucle infinito: Bucle cuya condici´n de terminaci´n nunca se cumple.
                                 o              o

cuerpo: 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.
           o

tabulador: 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.
                  a

secuencia 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.
                                                  a

encapsular: 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                                                                73

generalizar: 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 a

plan 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.
Todo Sobre El Lenguaje Python
Cap´
   ıtulo 7

Cadenas

7.1.     Un tipo de datos compuesto
Hasta el momento hemos visto tres tipos: int, float, y string. Las cadenas
son cuantitativamente diferentes de los otros dos porque est´n hechas de piezas
                                                            a
menores: caracteres.
Los tipos que comprenden piezas menores se llaman tipos de datos com-
puestos. Dependiendo de qu´ estemos haciendo, podemos querer tratar un tipo
                           e
compuesto 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 letra
La expresi´n fruta[1] selecciona el car´cter n´mero 1 de fruta. La variable
          o                            a      u
letra apunta al resultado. Cuando mostramos letra, nos encontramos con una
sorpresa:
a
La 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
                                                        o
contar 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           e
Si quiera la cer´sima letra de una cadena, simplemente pone 0, o cualquier
                o
expresi´n de valor 0, entre los corchetes:
       o
76                                                                     Cadenas

>>> letra = fruta[0]
>>> print letra
b

A la expresi´n entre corchetes se le llama ´
             o                                ındice. Un ´ ındice identifica a un
miembro de un conjunto ordenado, en este caso el conjunto de caracteres de la
cadena. El ´
           ındice indica cu´l quiere usted, de ah´ el nombre. Puede ser cualquier
                           a                     ı
expresi´n entera.
       o



7.2.     Longitud
La funci´n len devuelve el n´mero de caracteres de una cadena:
        o                   u

>>> fruta = "banana"
>>> len(fruta)
6

Para 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                                           o
string 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
                                                                 a
del 0 al 5. Para obtener el ultimo car´cter tenemos que restar 1 de longitud:
                            ´         a

longitud = len(fruta)
ultima = fruta[longitud-1]

De forma alternativa, podemos usar ´
                                   ındices negativos, que cuentan hacia atr´s
                                                                           a
desde 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 for
Muchos c´lculos incluyen el proceso de una cadena car´cter a car´cter. A me-
          a                                             a         a
nudo empiezan por el principio, seleccionan cada car´cter por turno, hacen algo
                                                    a
con ´l y siguen hasta el final. Este patr´n de proceso se llama recorrido. Una
    e                                   o
forma de codificar un recorrido es una sentencia while:

indice = 0
while indice < len(fruta):
  letra = fruta[indice]
7.3 Recorrido y el bucle for                                                      77

  print letra
  indice = indice + 1
Este bucle recorre la cadena y muestra cada letra en una l´        ınea distinta. La
condici´n del bucle es indice < len(fruta), de modo que cuando indice es
        o
igual a la longitud de la cadena, la condici´n es falsa y no se ejecuta el cuerpo del
                                            o
bucle. 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 Python
facilita una sintaxis alternativa m´s simple: el bucle for:
                                    a
for car in fruta:
  print car
Cada vez que recorremos el bucle, se asigna a la variable car el siguiente car´cter
                                                                              a
de la cadena. El bucle contin´a hasta que no quedan caracteres.
                             u
El ejemplo siguiente muestra c´mo usar la concatenaci´n junto a un bucle for
                               o                      o
para generar una serie abeced´rica. “Abeced´rica” es la serie o lista en la que
                               a              a
cada uno de los elementos aparece en orden alfab´tico. Por ejemplo, en el libro
                                                 e
de Robert McCloskey Make Way for Ducklings (Dejad paso a los patitos), los
nombres 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 + sufijo
La salida del programa es:
Jack
Kack
Lack
Mack
Nack
Oack
Pack
Qack
Por supuesto, esto no es del todo correcto, porque “Ouack” y “Quack” no est´n
                                                                           a
correctamente escritos.
78                                                                      Cadenas

     Como ejercicio, modifique el programa para corregir este error.



7.4.     Porciones de cadenas
Llamamos porci´n a un segmento de una cadena. La selecci´n de una porci´n
                  o                                     o              o
es 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       a
hasta 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   6
Si omite el primer ´ ındice (antes de los dos puntos), la porci´n comienza al
                                                                  o
principio de la cadena. Si omite el segundo ´
                                            ındice, la porci´n llega al final de la
                                                            o
cadena. As´ı:

>>> fruta = "banana"
>>> fruta[:3]
’ban’
>>> fruta[3:]
’ana’

¿Qu´ cree usted que significa s[:]?
   e



7.5.     Comparaci´n de cadenas
                  o
Los operadores de comparaci´n trabajan sobre cadenas. Para ver si dos cadenas
                           o
son iguales:
7.6 Las cadenas son inmutables                                             79

if palabra == "banana":
  print "S´, no tenemos bananas!"
           ı
Otras operaciones de comparaci´n son utiles para poner palabras en orden al-
                              o      ´
fab´tico:
   e
if palabra < "banana":
  print "Tu palabra," + palabra + ", va antes de banana."
elif palabra > "banana":
  print "Tu palabra," + palabra + ", va despu´s de banana."
                                             e
else:
  print "S´, no tenemos bananas!"
          ı
Sin embargo, deber´ usted ser consciente de que Python no maneja las may´scu-
                   ıa                                                   u
las y min´sculas como lo hace la gente. Todas las may´suculas van antes de la
         u                                            u
min´sculas. Como resultado de ello:
    u
Tu palabra, Zapato, va antes de banana.
Una forma com´n de abordar este problema es convertir las cadenas a un forma-
               u
to est´ndar, como pueden ser las min´sculas, antes de realizar la comparaci´n.
      a                             u                                      o
Un problema mayor es hacer que el programa se d´ cuenta de que los zapatos
                                                  e
no son frutas.


7.6.     Las cadenas son inmutables
Es tentador usar el operador [] en el lado izquierdo de una asignaci´n, con la
                                                                    o
intenci´n de cambiar un car´cter en una cadena. Por ejemplo:
       o                   a
saludo = "Hola, mundo"
saludo[0] = ’M’                 # ERROR!
print saludo
En lugar de presentar la salida Mola, mundo, este c´digo presenta el siguien-
                                                   o
te error en tiempo de ejecuci´n TypeError: object doesn’t support item
                              o
assignment.
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
                    a
variaci´n de la original:
       o
saludo = "Hola, mundo"
nuevoSaludo = ’M’ + saludo[1:]
print nuevoSaludo
80                                                                   Cadenas

Aqu´ la soluci´n es concatenar una nueva primera letra a una porci´n de saludo.
    ı         o                                                   o
Esta operaci´n no tiene efectos sobre la cadena original.
             o



7.7.     Una funci´n “encuentra”
                  o
¿Qu´ hace la siguiente funci´n?
   e                        o

def encuentra(cad, c):
  indice = 0
  while indice < len(cad):
    if cad[indice] == c:
      return indice
    indice = indice + 1
  return -1

En cierto sentido, encuentra es lo contrario del operador []. En lugar de tomar
un ´ındice y extraer el car´cter correspondiente, toma un car´cter y encuentra
                           a                                  a
el ´
   ındice donde aparece el car´cter. Si el car´cter no se encuentra, la funci´n
                                a             a                              o
devuelve -1.

Este es el primer ejemplo que hemos visto de una sentencia return dentro de
un bucle. Si cad[indice] == c, la funci´n vuelve inmediatamente, escapando
                                       o
del bucle prematuramente.

Si el car´cter no aparece en la cadena, el programa sale del bucle normalmente
         a
y devuelve -1.

Este patr´n de computaci´n se llama a veces un recorrido “eureka” porque en
         o              o
cuanto encontramos lo que buscamos, podemos gritar “¡Eureka!” y dejar de
buscar.

     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 conteo
El programa que sigue cuenta el n´mero de veces que la letra a aparece en una
                                 u
cadena:
7.9 El m´dulo “string”
        o                                                                    81

fruta = "banana"
cuenta = 0
for car in fruta:
  if car == ’a’:
    cuenta = cuenta + 1
print cuenta
Este programa muestra otro patr´n de computaci´n llamado contador. La
                                     o               o
variable cuenta se incializa a 0 y luego se incrementa cada vez que se encuentra
una a. (Incrementar es aumentar en uno; es lo contario de decrementar, y
sin relaci´n alguna con “excremento”, que es un nombre.) Al salir del bucle,
          o
cuenta 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”
             o
El 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 string
El m´dulo string incluye una funci´n llamada find que hace lo mismo que la
     o                              o
funci´n encuentra que escribimos. Para llamarla debemos especificar el nombre
     o
del 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 indice
1
Este ejemplo demuestra uno de los beneficios de los m´dulos: ayudan a evitar
                                                        o
las colisiones entre los nombres de las funciones predefinidas y las definidas por
el usuario. Al usar la notaci´n de punto podr´
                              o                 ıamos especificar qu´ versi´n de
                                                                     e    o
find queremos en caso de haberle daddo un nombre en ingl´s a nuestra funci´n.
                                                            e                o
En realidad, string.find es m´s general que nuestra versi´n. Para empezar,
                              a                          o
puede encontrar subcadenas, no s´lo caracteres:
                                o
82                                                                   Cadenas

>>> string.find("banana", "na")
2

Adem´s, acepta un argumento adicional que especifica el ´
     a                                                 ındice en el que deber´
                                                                             ıa
comenzar:

>>> string.find("banana", "na", 3)
4

O puede tomar dos argumentos adicionales que especifican un intervalo de ´
                                                                        ındi-
ces:

>>> string.find("sus", "s", 1, 2)
-1

En este ejemplo, la b´squeda falla porque la letra s no aparece en el intervalo
                     u
de ´
   ındices desde 1 hasta 2 (sin incluir 2).



7.10.      Clasificaci´n de caracteres
                     o
A menudo viene bien examinar un car´cter y comprobar si es una letra may´scu-
                                      a                                  u
la o min´scula, o si es un car´cter o un d´
         u                    a            ıgito. El m´dulo string proporciona
                                                      o
varias 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
            u
may´sculas. Pruebe lo que sigue y vea qu´ obtiene:
    u                                   e

>>> print string.lowercase
>>> print string.uppercase
>>> print string.digits

Podemos usar estas constantes y find para clasificar caracteres. Por ejemplo,
si find(lowercase, c) devuelve un valor que no sea -1, entonces c es una
min´scula:
    u

def esMinuscula(c):
  return find(string.lowercase, c) != -1

Alternativamente, podemos aprovecharnos del operador in, que determina si un
car´cter aparece en una cadena:
   a

def esMinuscula(c):
  return c in string.lowercase
7.11 Glosario                                                                 83

Como una alternativa m´s, podemos usar el operador de comparaci´n, aunque
                         a                                     o
esta soluci´n s´lo sea pr´ctica para el alfabeto ingl´s:
           o o           a                           e

def 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
                              o
imprima:

>>> print string.whitespace

Los caracteres de whitespace mueven el cursor sin imprimir nada. Crean los
espacios en blanco entre los caracteres visibles (al menos sobre papel blanco).
La constante string.whitespace contiene todos los caracteres de espacio en
blanco, 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
                      ´            o
un manual de referencia. Por otra parte, la Referencia de la Biblioteca de Python
s´ lo es. Junto con un mont´n m´s de documentaci´n, est´ disponible en el sitio
 ı                         o     a                 o       a
web de Python, www.python.org.



7.11.      Glosario
tipo 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.
                                      a

porci´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                                                                Cadenas

incrementar: 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 8

Listas

Una 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. Las
listas son similares a las cadenas de texto (strings), que son conjuntos ordenados
de caracteres, excepto en que los elementos de una lista pueden ser de cualquier
tipo. Las listas y las cadenas, y otras cosas que se comportan como conjuntos
ordenados, se llaman secuencias.



8.1.     Valores de una lista
Hay varias maneras de crear una nueva lista; la m´s sencilla es encerrar sus
                                                 a
elementos entre corchetes:
  [10, 20, 30, 40]
  ["spam", "el´stico", "golondrina"]
              a
El primer ejemplo es una lista de cuatro enteros. El segundo es una lista de tres
cadenas de texto. Los elementos de una lista no tienen por qu´ ser del mismo
                                                                e
tipo. La siguiente lista contiene una cadena, un n´mero con decimales y un
                                                     u
entero, y, maravilla de las maravillas, otra lista:
 ["hola", 2.0, 5, [10, 20]]
Se dice que una lista dentro de otra lista est´ anidada.
                                              a
Las listas que contienen n´meros enteros consecutivos son comunes, de manera
                          u
que Python proporciona una manera sencilla de crearlas:
  >>> range(1,5)
  [1, 2, 3, 4]
86                                                                       Listas

La funci´n range toma dos argumentos y devuelve una lista que contiene todos
        o
los enteros entre el primero y el segundo, ¡incluyendo el primero pero no el
segundo!

Hay dos formas alternativas para range. Con un solo argumento, crea una lista
que 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;
                                         a
a esto se le llama paso (step). Este ejemplo cuenta de 1 a 10 de dos en dos (con
pasos de 2).
  >>> range(1, 10, 2)
  [1, 3, 5, 7, 9]
Para terminar, hay una lista especial que no contiene elementos. Se la llama
lista vac´ y se representa [].
         ıa

Con todas estas maneras para crear listas, ser´ decepcionante que no pudi´ra-
                                               ıa                          e
mos asignar valores de listas a variables o pasar listas como par´metros a fun-
                                                                 a
ciones. 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 elementos
La sintaxis para acceder a los elementos de una lista es la misma que para
acceder a los caracteres de una cadena: el operador corchetes []. La expresi´n
                                                                            o
dentro de los corchetes especifica el ´
                                     ındice. Recuerde que los ´ındices siempre
comienzan en cero:
  print numeros[0]
  numeros[1] = 5
El operador [] puede aparecer en cualquier parte de una expresi´n. Cuando
                                                                o
aparece a la izquierda de una asignaci´n, cambia uno de los elementos de la
                                      o
lista, de manera que el “un´simo” elemento de numeros, que era 123, ahora es
                           e
5.

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 integer
Si intenta acceder (leer o modificar) un elemento que no existe, obtendr´ un
                                                                       a
error en tiempo de ejecuci´n:
                          o
>>> numeros[2] = 5
IndexError: list assignment index out of range
Si 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 range
numeros[-1] es el ultimo elemento de la lista, numeros[-2] es el pen´ltimo, y
                  ´                                                 u
numeros[-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   + 1
Este bucle while cuenta desde 0 hasta 4. Cuando la variable de bucle vale 4, la
condici´n falla y acaba el bucle. Por tanto, el cuerpo del bucle s´lo se ejecuta
       o                                                          o
cuando 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                                 o
recorrido de lista.



8.3.     Longitud (tama˜ o) de una lista
                       n
La funci´n len toma una lista y devuelve su tama˜o. Es una buena idea usar
         o                                          n
este valor como l´
                 ımite superior de un bucle, en lugar de una constante. De esta
manera, si el tama˜o de la lista cambia, no habr´ que estar haciendo cambios
                   n                              a
en 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 + 1
La 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
              ´                                                             o
falla 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 anidada
cuenta 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 lista
in es un operador booleano que comprueba la pertenencia a una secuencia. Lo
usamos en la Secci´n 7.10 con las cadenas, pero tambi´n funciona con las listas
                   o                                 e
y otras secuencias:

>>> jinetes = [’guerra’, ’hambre’, ’peste’, ’muerte’]
>>> ’peste’ in jinetes
1
>>> ’libertinaje’ in jinetes
0

Como “peste” es un miembro de la lista jinetes, el operador in devuelve
verdadero. Como “libertinaje” no est´ en la lista, in devuelve falso.
                                    a
Podemos usar not en combinaci´n con in para comprobar si un elemento no es
                             o
miembro de una lista:

>>> ’libertinaje’ not in jinetes
1



8.5.     Listas y bucles for
El bucle for que vimos en la Secci´n 7.3 tambi´n funciona con las listas. La
                                    o         e
sintaxis generalizada de un bucle for es:
8.6 Operaciones con listas                                                   89

   for VARIABLE in LISTA:
      CUERPO
Esta sentencia es equivalente a:
   i = 0
   while i < len(LISTA):
      VARIABLE = LISTA[i]
      CUERPO
      i = i + 1
El bucle for es m´s conciso porque podemos eliminar la variable de bucle, i.
                   a
Aqu´ tenemos el bucle anterior con un bucle for:
     ı
   for jinete in jinetes:
      print jinete
M´s a´n, casi se lee igual que en espa˜ol, “Para (cada) jinete en (la lista de)
   a u                                 n
jinetes, 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
                                     u
ejemplo expresa su entusiasmo por diferentes frutas.


8.6.     Operaciones con listas
El 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 repetido
cuatro veces. En el segundo ejemplo, la lista [1, 2, 3] se repite tres veces.
90                                                                      Listas

8.7.     Porciones (slices)
Las operaciones de porciones que vimos en la Secci´n 7.4 tambi´n funcionan en
                                                  o           e
sobre 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 mutables
A diferencia de las cadenas, las listas son mutables, lo que significa que pode-
mos cambiar sus elementos. Podemos modificar uno de sus elementos usando el
operador 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     ıa
posici´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 lista
El uso de porciones para borrar elementos de una lista puede ser extra˜o, y
                                                                          n
por ello propicio a los errores. Python nos da una alternativa que resulta m´s
                                                                            a
legible.

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 tiempo
de 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 no
inclu´
     ıdo, el segundo ´
                     ındice.



8.10.      Objetos y valores
Si 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
   a
podemos saber si est´n apuntando a la misma cadena.
                     a

Hay dos posibles estados:
92                                                                      Listas


                a       "banana"            a
                                                    "banana"
                b       "banana"            b


En 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 una
variable.
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
        o
al mismo objeto.
   >>> id(a)
   135044008
   >>> id(b)
   135044008
En este caso, las dos veces obtenemos el mismo identificador, lo que significa
que Python s´lo cre´ una cadena y ambas variables, a y b, apuntan a ella.
              o     o
Como las cadenas de texto son inmutables, no hay diferencia pr´ctica entre los
                                                                 a
dos 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)
  135041704
De 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, ambas
variables se refieren al mismo objeto:
8.12 Clonar listas                                                           93

  >>> a = [1, 2, 3]
  >>> b = a
En este caso, el diagrama de estados ser´ como ´ste:
                                        ıa     e

                              a
                                       [ 1, 2, 3 ]
                              b


Como la misma lista tiene dos nombres diferentes, a y b, podemos decir que se
le 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-
                 a
bles. Por supuesto, no hay problema con los objetos inmutables. Por ello Python
se toma la libertad de poner alias a las cadenas cuando ve una oportunidad de
economizar.


8.12.       Clonar listas
Si queremos modificar una lista y mantener una copia del original, necesitaremos
ser capaces de hacer una copia de la lista en s´ no s´lo de su referencia. Este
                                               ı,    o
proceso 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                                                  o
consta 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                                                                          Listas

8.13.      Listas como par´meteros
                          a
Cuando se pasa una lista como argumento, en realidad se pasa una referencia
a ella, no una copia de la lista. Por ejemplo, la funci´n cabeza toma una lista
                                                       o
como 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)
  1
El par´metro lista y la variable numeros son alias de un mismo objeto. El
      a
diagrama de estado es as´
                        ı:


                      __main__     numbers
                                                   [ 1, 2, 3 ]
                           head          list

Como el objeto lista est´ compartido por dos marcos, lo dibujamos entre ambos.
                        a
Si la funci´n modifica una lista pasada como par´metro, el que hizo la llamada
           o                                   a
ver´ 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,
            o
cola 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                                                          95

Como el valor de retorno se cre´ con una porci´n, es una lista. La creaci´n de
                               o              o                          o
rest, as´ como cualquier cambio posterior en rest, no afectar´ a numbers.
        ı                                                    a



8.14.      Listas anidadas
Una 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 elementos
de la lista anidada, podemos proceder en dos pasos:
  >>> elt = lista[3]
  >>> elt[0]
  10
O podemos combinarlos:
  >>> lista[3][1]
  20
Los operadores corchete se eval´an de izquierda a derecha, as´ que esta expresi´n
                               u                             ı                 o
saca el tri-´simo elemento de lista y luego extrae el un´simo elemento de ella.
            e                                           e



8.15.      Matrices
Es com´n usar listas anidadas para representar matrices. Por ejemplo, la matriz:
      u

                                    1 2 3
                                    4 5 6
                                    7 8 9


puede 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 la
matriz. 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                                                                       Listas

El primer ´
          ındice escoge la fila y el segundo la columna. Aunque esta manera de
representar matrices es com´n, no es la unica posibilidad. Una peque˜a variaci´n
                            u           ´                           n         o
consiste en usar una lista de columnas en lugar de flas. M´s adelante veremos
                                                            a
una alternativa m´s radical usando un diccionario.
                  a



8.16.      Cadenas y listas
Dos de las funciones m´s utiles del m´dulo string tienen que ver con listas
                       a ´            o
de cadenas. La funci´n split divide una cadena en una lista de palabras. Por
                     o
defecto, cualquier n´mero de caracteres de espacio en blanco se considera un
                    u
l´
 ı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 especificar
qu´ caracteres se usar´n como l´
  e                   a        ımites de palabra. El siguiente ejemplo usa la
cadena 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
        o
los 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                                                                97

8.17.      Glosario
lista: 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.
        u

clonar: 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.
Todo Sobre El Lenguaje Python
Cap´
   ıtulo 9

Tuplas

9.1.     Mutabilidad y tuplas
Hasta ahora, ha visto dos tipos compuestos: cadenas, que est´n hechas de ca-
                                                              a
racteres, y listas, que est´n hechas de elementos de cualquier tipo. Una de las
                           a
diferencias que se˜alamos es que los elementos de una lista se pueden modifi-
                    n
car, pero los caracteres de una cadena no. En otras palabras, las cadenas son
inmutables y las listas son mutables.
En Python hay otro tipo llamado tupla que es similar a una lista salvo en que
es inmutable. Sint´cticamente, una tupla es una lista de valores separados por
                  a
comas:
>>> tupla = ’a’, ’b’, ’c’, ’d’, ’e’
Aunque no es necesario, la convenci´n dice que hay que encerrar las tuplas entre
                                   o
par´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                                                                       Tuplas

Dejando a un lado las cuestiones de sintaxis, las operaciones sobre las tuplas
son las mismas que sobre las listas. El operador ´
                                                 ındice selecciona un elemento
de 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 un
error:
>>> tupla[0] = ’A’
TypeError: object doesn’t support item assignment
Por 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
                 o
De vez en cuando, es util intercambiar los valores de dos variables. Para ha-
                      ´
cerlo con sentencias de asignaci´n convencionales debemos usar una variable
                                o
temporal. Por ejemplo, para intercambiar a y b:
>>> temp = a
>>> a = b
>>> b = temp
Si tenemos que hacer esto a menudo, esta aproximaci´n resulta aparatosa. Pyt-
                                                   o
hon proporciona una forma de asignaci´n de tuplas que soluciona este pro-
                                       o
blema elegantemente:
>>> a, b = b, a
El lado izquierdo es una tupla de variables, el lado derecho es una tupla de
valores. Cada valor se asigna a su respectiva variable. Todas las expresiones del
lado derecho se eval´an antes de las asignaciones. Esta caracter´
                    u                                            ıstica hace de la
asignaci´n de tuplas algo muy vers´til.
         o                          a
9.3 Tuplas como valor de retorno                                            101

Naturalmente, el n´mero de variables a la izquierda y el n´mero de valores a la
                  u                                       u
derecha deben ser iguales:

>>> a, b, c, d = 1, 2, 3
ValueError: unpack tuple of wrong size



9.3.     Tuplas como valor de retorno
Las funciones pueden devolver tuplas como valor de retorno. Por ejemplo,
podr´
    ıamos escribir una funci´n que intercambie dos par´metros:
                            o                         a

def intercambio(x, y):
  return y, x

Luego 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.o
De hecho, existe un peligro al intentar encapsular intercambio, y es el tentador
error que sigue:

def intercambio(x, y):            # versi´n incorrecta
                                         o
  x, y = y, x

Si 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 x
se 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
          o
intentamos. 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.
                                      e



9.4.     N´ meros aleatorios
          u
La mayor parte de los programas hacen lo mismo cada vez que los ejecutamos,
por lo que se dice que son deterministas. Normalmente el determinismo es
una cosa buena, ya que esperamos que un c´lculo nos d´ siempre el mismo
                                            a          e
102                                                                     Tuplas

resultado. Para algunas aplicaciones, sin embargo, queremos que el computador
sea impredecible. El ejemplo obvio son los juegos, pero hay m´s.
                                                             a
Hacer 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 generar
n´meros aleatorios y usarlos para determinar el resultado del programa. Python
 u
proporciona una funci´n interna que genera n´meros pseudoaleatorios, que
                      o                        u
no son verdaderamente aleatorios en un sentido matem´tico, pero servir´n para
                                                       a                 a
nuestros prop´sitos.
             o
El m´dulo random contiene una funci´n llamada random que devuelve un n´mero
     o                              o                                    u
en coma flotante entre 0,0 y 1,0. Cada vez que usted llama a random obtiene el
siguiente n´mero de una larga serie. Para ver un ejemplo, ejecute este bucle:
           u
import random

for i in range(10):
  x = random.random()
  print x
Para 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
                   u
El primer paso es generar una lista de valores aleatorios. listaAleatorios
acepta un par´metro entero y devuelve una lista de n´meros aleatorios de la
              a                                        u
longitud 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
                                       u
una referencia a la lista completa:
def listaAleatorios(n):
  s = [0] * n
  for i in range(n):
    s[i] = random.random()
  return s
Vamos a probar esta funci´n con una lista de ocho elementos. A la hora de
                          o
depurar es una buena idea empezar con algo peque˜o.
                                                n
9.6 Conteo                                                               103

>>> listaAleatorios(8)
0.15156642489
0.498048560109
0.810894847068
0.360371157682
0.275119183077
0.328578797631
0.759199803101
0.800367163582

Se supone que los n´meros generados por random est´n distribuidos uniforme-
                    u                                a
mente, lo que significa que cada valor es igualmente probable.
Si dividimos el intervalo de valores posibles en “baldes” de igual tama˜o y
                                                                       n
contamos el n´mero de veces que un valor cae en cada balde, deber´
             u                                                   ıamos tener
m´s o menos el mismo n´mero en todos.
  a                     u
Podemos contrastar esta teor´ escribiendo un programa que divida el intervalo
                            ıa
en baldes y contando el n´mero de valores en cada uno.
                         u



9.6.     Conteo
Un buen enfoque sobre problemas como ´ste es dividir el problema en subpro-
                                     e
blemas 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                   u
veces que un valor cae en un intervalo dado. Eso nos suena. En la Secci´n 7.8
                                                                       o
escribimos un programa que recorr´ una cadena de texto y contaba el n´mero
                                  ıa                                   u
de veces que aparec´ una letra determinada.
                   ıa
As´ podemos hacerlo copiando el programa viejo y adapt´ndolo al problema
   ı,                                                 a
actual. El programa original era:

cuenta = 0
for car in fruta:
  if car == ’a’:
    cuenta = cuenta + 1
print cuenta

El primer paso es sustituir fruta con lista y car con num. Esto no cambia el
programa, s´lo lo hace m´s legible.
           o             a
El segundo paso es cambiar la comprobaci´n. No estamos interesados en encon-
                                          o
trar letras. Queremos ver si num est´ entre los valores de minimo y maximo.
                                    a
104                                                                     Tuplas

cuenta = 0
for num in lista
  if minimo < num < maximo:
    cuenta = cuenta + 1
print cuenta
El ultimo paso es encapsular este c´digo en una funci´n llamada enElBalde.
   ´                                 o                 o
Los par´metros son la lista y los valores minimo y maximo.
        a
def enElBalde(lista, minimo, maximo):
  cuenta = 0
  for num in lista:
    if minimo < num < maximo:
      cuenta = cuenta + 1
  return cuenta
Copiar y modificar un programa existente nos facilita escribir esta funci´n r´pi-
                                                                        o a
damente y nos ahorra un mont´n de tiempo de depuraci´n. Este plan de desa-
                              o                         o
rrollo se llama coincidencia de esquemas. Si se encuentra trabajando en un
problema que ya solucion´, reutilice la soluci´n.
                         o                    o


9.7.     Muchos baldes
Tal como aumenta el n´mero de baldes, enElBalde se hace un tanto dif´ de
                      u                                             ıcil
manejar. Con dos baldes, no est´ mal:
                               a
bajo = 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 de
cada balde.
Empezaremos por solucionar el segundo problema. Si el n´mero de baldes es
                                                        u
numBaldes, la anchura de cada balde es 1.0 / numBaldes.
Usaremos un bucle para calcular el intervalo de cada balde. La variable del
bucle, i, cuenta de 1 a numBaldes-1:
9.7 Muchos baldes                                                            105

anchuraBalde = 1.0 / numBaldes
for i in range(numBaldes):
  minimo = i * anchuraBalde
  maximo = minimo + anchuraBalde
  print minimo, "hasta", maximo
Para calcular el l´
                  ımite inferior de cada balde, multiplicamos la variable de bucle
por la anchura de balde. El l´ ımite superior est´ a tan s´lo una anchuraBalde.
                                                 a        o
Con numBaldes = 8, la salida es:
0.0 hasta 0.125
0.125 hasta 0.25
0.25 hasta 0.375
0.375 hasta 0.5
0.5 hasta 0.625
0.625 hasta 0.75
0.75 hasta 0.875
0.875 hasta 1.0
Puede confirmar que todos los bucles tienen la misma anchura, que no se solapan
y que cubren todo el intervalo entre 0,0 y 1,0.
Volvamos ahora al primer problema. Necesitamos un modo de almacenar ocho
enteros, usando la variable de bucle para se˜alarlos uno por uno. En estos mo-
                                            n
mentos deber´ usted estar pensando “¡Lista!”.
             ıa
Debemos crear la lista de baldes fuera del bucle, porque s´lo queremos hacer-
                                                          o
lo una vez. Dentro del bucle, podemos llamar repetidamente a enElBalde y
actualizar el i-´simo elemento de la lista:
                e
numBaldes = 8
baldes = [0] * numBaldes
anchuraBalde = 1.0 / numBaldes
for i in range(numBaldes):
  minimo = i * anchuraBalde
  maximo = minimo + anchuraBalde
  baldes[i] = enElBalde(lista, minimo, maximo)
print baldes
Con 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                              a
Por lo menos, est´n lo bastante cerca como para que podamos pensar que el
                 a
generador 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.
             u


9.8.     Una soluci´n en una sola pasada
                   o
Aunque este programa funciona, no es tan eficiente como podr´ ser. Cada vez
                                                            ıa
que llama a enElBalde recorre la lista entera. Con el aumento del n´mero de
                                                                   u
baldes, llega a ser un mont´n de recorridos.
                           o
Ser´ mejor hacer una sola pasada por la lista y calcular para cada valor el ´
   ıa                                                                       ındice
del 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 la
anchuraBalde 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 que
cae.
Como el problema es el inverso del anterior, podemos suponer que deber´ ıamos
dividir por anchuraBalde en lugar de multiplicar. La suposici´n es correcta.
                                                             o
Como anchuraBalde = 1.0 / numBaldes, dividir por anchuraBalde es lo mis-
mo que multiplicar por numBaldes. Si multiplicamos un n´mero del intervalo
                                                         u
que va de 0,0 a 1,0 por numBaldes, obtenemos un n´mero del intervalo entre
                                                   u
0,0 y numBaldes. Si redondeamos ese n´mero al entero inferior obtendremos
                                      u
exactamente lo que estamos buscando, un ´
                                        ındice de balde:
numBaldes = 8
baldes = [0] * numBaldes
for i in lista:
  indice = int(i * numBaldes)
  baldes[indice] = baldes[indice] + 1
Usamos 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
                                                     e
negativo como mayor que len(baldes)-1)?
Una lista como baldes que contiene conteos del n´mero de valores en cada
                                                u
intervalo 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                                                              107

9.9.     Glosario
tipo 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.
                                       a

histograma: 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
Todo Sobre El Lenguaje Python
Cap´
   ıtulo 10

Diccionarios

Los 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
                                                                             a
error.
Los diccionarios son similares a otros tipos compuestos excepto en que pueden
usar como ´ındice cualquier tipo inmutable. A modo de ejemplo, crearemos un
diccionario 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   n
elementos. 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-
                     o
naciones a˜aden nuevos elementos al diccionario. Podemos presentar el valor
           n
actual del diccionario del modo habitual:
>>> print ing_a_esp
{’one’: ’uno’, ’two’: ’dos’}
Los elementos de un diccionario aparecen en una lista separada por comas.
Cada entrada contiene un ´   ındice y un valor separado por dos puntos (:). En un
diccionario, los ´
                 ındices se llaman claves, por eso los elementos se llaman pares
clave-valor.
Otra forma de crear un diccionario es dando una lista de pares clave-valor con
la 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-
                             a
cuparnos por el orden, ya que los elementos de un diccionario nunca se indexan
con ´
    ındices enteros. En lugar de eso, usamos las claves para buscar los valores
correspondientes:
>>> 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 diccionarios
La sentencia del elimina un par clave-valor de un diccionario. Por ejemplo, el
diccionario siguiente contiene los nombres de varias frutas y el n´mero de esas
                                                                  u
frutas 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-
                         a
ventario 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                                          u
clave-valor:
>>> len(inventario)
4
10.2 M´todos del diccionario
      e                                                                     111

10.2.      M´todos del diccionario
            e
Un m´todo es similar a una funci´n, acepta par´metros y devuelve un valor,
      e                               o             a
pero la sintaxis es diferente. Por ejemplo, el m´todo keys acepta un diccionario
                                                e
y devuelve una lista con las claves que aparecen, pero en lugar de la sintaxis de
la 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                                           o
nombre del objeto al que se va a aplicar la funci´n, ing a esp. Los par´ntesis
                                                 o                     e
indican que este m´todo no admite par´metros.
                  e                   a

La llamda a un m´todo se denomina invocaci´n; en este caso, dir´
                 e                            o                ıamos que
estamos 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
     e
del 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
                                           e
la lista son tuplas.

Si un m´todo acepta un argumento, usa la misma sintaxis que una llamada
        e
a una funci´n. Por ejemplo, el m´todo has key acepta una clave y devuelve
           o                       e
verdadero (1) si la clave aparece en el diccionario:

>>> ing_a_esp.has_key(’one’)
1
>>> ing_a_esp.has_key(’deux’)
0

Si usted invoca un m´todo sin especificar un objeto, provoca un error. En este
                     e
caso, el mensaje de error no es de mucha ayuda:

>>> has_key(’one’)
NameError: has_key
112                                                                Diccionarios

10.3.      Asignaci´n de alias y copiado
                   o
Debe 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 el
m´todo copy. Por ejemplo, opuestos es un diccionario que contiene pares de
  e
opuestos:

>>> 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 copia
nueva del mismo diccionario. Si modificamos alias, opuestos tambi´n resulta
                                                                    e
cambiado:

>>> alias[’derecho’] = ’sentado’
>>> opuestos[’derecho’]
’sentado’

Si modificamos copia, opuestos no var´
                                    ıa:

>>> copia[’derecho’] = ’privilegio’
>>> opuestos[’derecho’]
’sentado’



10.4.      Matrices dispersas
En la Secci´n 8.14 usamos una lista de listas para representar una matriz. Es
            o
una buena opci´n para una matriz en la que la mayor´ de los valores es diferente
               o                                   ıa
de 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   0

La representaci´n de la lista contiene un mont´n de ceros:
               o                              o

matriz = [ [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
                                                                       o
misma 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
 o
de cero. Cada clave es una tupla, y cada valor es un entero.
Para acceder a un elemento de la matriz, podemos usar el operador []:

matriz[0,3]
1

F´ıjese en que la sintaxis para la representaci´n por medio del diccionario no es
                                               o
la 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 error
porque 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)
1

El primer argumento es la clave; el segundo argumento es el valor que debe
devolver get en caso de que la clave no est´ en el diccionario:
                                           e

>>> matriz.get((1,3), 0)
0

get mejora sensiblemente la sem´ntica del acceso a una matriz dispersa. L´stima
                               a                                         a
de sintaxis.



10.5.      Pistas
Si estuvo jugando con la funci´n fibonacci de la Secci´n 5.7, es posible que
                              o                       o
haya notado que cuanto m´s grande es el argumento que le da, m´s tiempo le
                          a                                     a
114                                                                           Diccionarios

cuesta ejecutarse. M´s a´n, el tiempo de ejecuci´n aumenta muy r´pidamente.
                    a u                         o               a
En nuestra m´quina, fibonacci(20) acaba instant´neamente, fibonacci(30)
              a                                    a
tarda m´s o menos un segundo, y fibonacci(40) tarda una eternidad.
        a
Para entender por qu´, observe este gr´fico de llamadas de fibonacci con
                    e                 a
n=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       0


Un gr´fico de llamadas muestra un conjunto de cajas de funci´n con l´
      a                                                     o       ıneas que
conectan cada caja con las cajas de las funciones a las que llama. En lo alto
del gr´fico, fibonacci con n=4 llama a fibonacci con n=3 y n=2. A su vez,
      a
fibonacci 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                                                               o
ineficaz 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
                o
almacen´ndolos en un diccionario. A un valor que ya ha sido calculado y alma-
       a
cenado para un uso posterior se le llama pista. Aqu´ hay una implementaci´n
                                                     ı                       o
de 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 nuevoValor
El 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
                                                  o
a 1 y 1 corresponde a 1.
10.6 Enteros largos                                                         115

Siempre 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 ı,          o
sin hacer m´s llamadas recursivas. Si no, tiene que calcular el nuevo valor. El
            a
nuevo valor se a˜ade al diccionario antes de que la funci´n vuelva.
                n                                         o
Con esta versi´n de fibonacci, nuestra m´quina puede calcular fibonacci(40)
              o                          a
en un abrir y cerrar de ojos. Pero cuando intentamos calcular fibonacci(50),
nos encontramos con otro problema:
>>> fibonacci(50)
OverflowError: integer addition
La respuesta, como ver´ en un momento, es 20.365.011.074. El problema es
                      a
que este n´mero es demasiado grande para caber en un entero de Python. Se
          u
desborda. Afortunadamente, hay una soluci´n f´cil para este problema.
                                         o a



10.6.      Enteros largos
Python proporciona un tipo llamado long int que puede manejar enteros de
cualquier tama˜o. Hay dos formas de crear un valor long int. Una es escribir
              n
un 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
                        o
cualquier tipo num´rico e incluso cadenas de d´
                     e                          ıgitos:
>>> long(1)
1L
>>> long(3.9)
3L
>>> long(’57’)
57L
Todas 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)
20365011074L
Simplemente cambiando el contenido inicial de anteriores cambiamos el com-
portamiento de fibonacci. Los primeros dos n´meros de la secuencia son long
                                               u
ints, 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 letras
En el cap´ıtulo 7 escribimos una funci´n que contaba el n´mero de apariciones
                                       o                    u
de una letra en una cadena. Una versi´n m´s gen´rica de este problema es crear
                                      o     a      e
un histograma de las letras de la cadena, o sea, cu´ntas veces aparece cada letra.
                                                   a
Ese 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       a
para 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, el
diccionario contiene pares de letras y sus frecuencias.
Puede ser m´s atractivo mostrar el histograma en orden alfab´tico. Podemos
             a                                              e
hacerlo 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                                   e
a listas que hemos visto. Hay varios m´s, como append, extend, y reverse.
                                      a
Consulte la documentaci´n de Python para ver los detalles.
                        o


10.8.      Glosario
diccionario: 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                                                             117

clave: 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”.
              o

m´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.
                      e

pista: 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
Todo Sobre El Lenguaje Python
Cap´
   ıtulo 11

Archivos y excepciones

Cuando un programa se est´ ejecutando, sus datos est´n en la memoria. Cuando
                          a                         a
un programa termina, o se apaga el computador, los datos de la memoria desa-
parecen. Para almacenar los datos de forma permanente debe usted ponerlos en
un archivo. Normalmente los archivos se guardan en un disco duro, disquete o
CD-ROM.

Cuando hay un gran n´mero de archivos, suelen estar organizados en directo-
                     u
rios (tambi´n llamados “carpetas”). Cada archivo se identifica con un nombre
           e
unico, o una combinaci´n de nombre de archivo y nombre de directorio.
´                     o

Leyendo y escribiendo archivos, los programas pueden intercambiar informaci´n
                                                                           o
entre ellos y generar formatos imprimibles como PDF.

Trabajar con archivos se parece mucho a trabajar con libros. Para usar un
libro, tiene que abrirlo. Cuando ha terminado, tiene que cerrarlo. Mientras el
libro est´ abierto, puede escribir en ´l o leer de ´l. En cualquier caso, sabe en
          a                            e           e
qu´ lugar del libro se encuentra. Casi siempre lee el libro seg´n su orden natural,
   e                                                           u
pero tambi´n puede ir saltando de una p´gina a otra.
            e                              a

Todo esto sirve tambi´n para los archivos. Para abrir un archivo, especifique su
                     e
nombre e indique si quiere leer o escribir.

La apertura de un archivo crea un objeto archivo. En este ejemplo, la variable
f apunta al nuevo objeto archivo.

>>> f = open("test.dat","w")
>>> print f
<open file ’test.dat’, mode ’w’ at fe820>
120                                                  Archivos y excepciones

La funci´n open toma dos argumentos. El primero es el nombre del archivo y
        o
el segundo es el modo. El modo ’w’ (write) significa que lo estamos abriendo
para escribir.
Si no hay un archivo llamado test.dat se crear´. Si ya hay uno, el archivo que
                                              a
estamos escribiendo lo reemplazar´.
                                 a
Al imprimir el objeto archivo, vemos el nombre del archivo, el modo y la loca-
lizaci´n del objeto.
      o
Para meter datos en el archivo invocamos al m´todo write sobre el objeto
                                             e
archivo:
>>> 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 deja
el archivo listo para leer:
>>> f.close()
Ya podemos abrir el archivo de nuevo, esta vez para lectura, y poner su contenido
en una cadena. Esta vez el argumento de modo es ’r’ (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
                         e
el archivo completo:
>>> text = f.read()
>>> print text
Ya es horade cerrar el archivo
No hay un espacio entre “hora” y “de” porque no escribimos un espacio entre
las cadenas.
read tambi´n puede aceptar un argumento que le indica cu´ntos caracteres leer:
          e                                             a
>>> f = open("test.dat","r")
>>> print f.read(7)
Ya es h
Si 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
                  o
cincuenta 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()
  return
La sentencia break es nueva. Su ejecuci´n interrumpe el bucle; el flujo de la
                                         o
ejecuci´n pasa a la primera sentencia tras el bucle.
       o
En 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 texto
Un 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. Como
Python est´ dise˜ado espec´
           a    n          ıficamente para procesar archivos de texto, propor-
ciona m´todos que facilitan la tarea.
        e
Para hacer una demostraci´n, crearemos un archivo de texto con tres l´
                           o                                         ıneas de
texto 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
      e
de 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
                           a
aparecen con comillas y el car´cter de salto de l´
                              a                  ınea aparece como la secuencia
de escape
012.

Al final del archivo, readline devuelve una cadena vac´ y readlines devuelve
                                                     ıa
una lista vac´
             ıa:

>>> print f.readline()

>>> print f.readlines()
[]

Lo que sigue es un ejemplo de un programa de proceso de l´
                                                         ıneas. filtraArchivo
hace 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()
  return

La sentencia continue termina la iteraci´n actual del bucle, pero sigue haciendo
                                         o
bucles. El flujo de ejecuci´n pasa al principio del bucle, comprueba la condici´n
                          o                                                   o
y contin´a en consecuencia.
        u
11.2 Escribir variables                                                     123

As´ si texto es una cadena vac´ el bucle termina. Si el primer car´cter de
  ı,                            ıa,                                     a
texto es una almohadilla, el flujo de ejecuci´n va al principio del bucle. S´lo si
                                            o                              o
ambas condiciones fallan copiamos texto en el archivo nuevo.



11.2.      Escribir variables
El 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
                                                                            a
f´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
                    o
el operador de formato.
El primer operando es la cadena de formato, y el segundo operando es una
tupla de expresiones. El resultado es una cadena que contiene los valores de las
expresiones, formateados de acuerdo a la cadena de formato.
A modo de ejemplo simple, la secuencia de formato ’%d’ significa que la
primera 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 de
formato, 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 como
un n´mero en coma flotante, y ’%s’ formatea el siguiente elemento como una
    u
cadena:

>>> "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 excepciones

Por 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                                                              u
secuencias de formato de la cadena. Igualmente, los tipos de las expresiones
deben coincidir con las secuencias de formato:

>>> "%d %d   %d" % (1,2)
TypeError:   not enough arguments for format string
>>> "%d" %   ’d´lares’
               o
TypeError:   illegal argument type for built-in operation

En el primer ejemplo, no hay suficientes expresiones; en el segundo, la expresi´n
                                                                              o
es de un tipo incorrecto.
Para tener m´s control sobre el formato de los n´meros, podemos detallar el
             a                                   u
n´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 que
ocupar´ el n´mero. Si el valor necesita menos d´
       a     u                                  ıgitos, se a˜aden espacios en
                                                            n
blanco delante del n´mero. Si el n´mero de espacios es negativo, se a˜aden los
                    u             u                                  n
espacios 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                            u
flotante:

>>> "%12.2f" % 6.1
’        6.10’

En este ejemplo, el resultado ocupa doce espacios e incluye dos d´
                                                                 ıgitos tras la
coma. Este formato es util para imprimir cantidades de dinero con las comas
                        ´
alineadas.
Imagine, por ejemplo, un diccionario que contiene los nombres de los estudiantes
como clave y las tarifas horarias como valores. He aqu´ una funci´n que imprime
                                                      ı          o
el 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                     n
contenido:
>>> tarifas = {’mar´a’: 6.23, ’jos´’: 5.45, ’jes´s’: 4.25}
                   ı              e             u
>>> informe (tarifas)
jos´
   e                         5.45
jes´s
   u                         4.25
mar´a
   ı                         6.23
Controlando la anchura de cada valor nos aseguramos de que las columnas van a
quedar alineadas, siempre que los nombres tengan menos de veinti´n caracteres
                                                                 u
y las tarifas sean menos de mil millones la hora.


11.3.      Directorios
Cuando usted crea un archivo nuevo abri´ndolo y escribiendo, el nuevo archi-
                                         e
vo va al directorio en uso (aqu´l en el que etuviese al ejecutar el programa).
                               e
Del mismo modo, cuando abre un archivo para leerlo, Python lo busca en el
directorio en uso.
Si quiere abrir un archivo de cualquier otro sitio, tiene que especificar la ruta
del archivo, que es el nombre del directorio (o carpeta) donde se encuentra ´ste:
                                                                            e
>>>    f = open("/usr/share/dict/words","r")
>>>    print f.readline()
Aarhus
Este ejemplo abre un archivo llamado words que est´ en un directorio llamado
                                                  a
dict, que est´ en share, que est´ en usr, que est´ en el directorio de nivel
               a                 a                a
superior del sistema, llamado /.
No puede usar / como parte del nombre de un archivo; est´ reservado como
                                                        a
delimitador entre nombres de archivo y directorios.
El archivo /usr/share/dict/words contiene una lista de palabras en orden
alfab´tico, la primera de las cuales es el nombre de una universidad danesa.
     e


11.4.      Encurtido
Para poner valores en un archivo, debe convertirlos en cadenas. Ya ha visto
c´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. Ha
perdido la informaci´n del tipo de dato original. En realidad, no puede distinguir
                    o
d´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                          o
y 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
                                                e
archivo 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 que
volcamos ah´
           ı:
>>> f = open("test.pck","r")
>>> x = pickle.load(f)
>>> x
12.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 su
tipo original.


11.5.      Excepciones
Siempre que ocurre un error en tiempo de ejecuci´n, se crea una excepci´n.
                                                o                      o
Normalmente el programa se para y Pythton presenta un mensaje de error.
11.5 Excepciones                                                             127

Por ejemplo, la divisi´n por cero crea una excepci´n:
                      o                           o

>>> print 55/0
ZeroDivisionError: integer division or modulo

Un elemento no existente en una lista hace lo mismo:

>>> a = []
>>> print a[5]
IndexError: list index out of range

O el acceso a una clave que no est´ en el diccionario:
                                  a

>>> b = {}
>>> print b[’qu´’]
               e
KeyError: qu´
            e

En cada caso, el mensaje de error tiene dos partes: el tipo de error antes de
los dos puntos y detalles sobre el error depu´s de los dos puntos. Normalmente
                                             e
Python tambi´n imprime una traza de d´nde se encontraba el programa, pero
              e                           o
la hemos omitido en los ejemplos.
A veces queremos realizar una operaci´n que podr´ provocar una excepci´n,
                                     o          ıa                    o
pero no queremos que se pare el programa. Podemos manejar la excepci´n o
usando las sentencias try y except.
Por ejemplo, podemos preguntar al usuario por el nombre de un archivo y luego
intentar abrirlo. Si el archivo no existe, no queremos que el programa se pare;
queremos manejar la excepci´n. o

nombreArch = raw_input(’Introduce un nombre de archivo: ’)
try:
  f = open (nombreArch, "r")
except:
  print ’No hay ning´n archivo que se llame’, nombreArch
                    u

La 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                                                                o
ejecuta las sentencias de la rama except y despu´s contin´a.
                                                  e         u
Podemos encapsular esta capacidad en una funci´n: existe acepta un nombre
                                                  o
de 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 0

Puede usar m´ltiples bloques except para manejar diferentes tipos de excep-
             u
ciones. El Manual de Referencia de Python contiene los detalles.

Si su programa detecta una condici´n de error, puede hacer que lance (raise en
                                   o
ingl´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
                                                                     a
cualquier raz´n, lanzamos una excepci´n.
             o                        o

def 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 x

La sentencia raise acepta dos argumentos: el tipo de excepci´n e informaci´n
                                                             o             o
espec´
     ıfica acerca del error. ErrorN´meroMalo es un nuevo tipo de excepci´n que
                                  u                                    o
hemos inventado para esta aplicaci´n.
                                   o

Si la funci´n llamada tomaNumero maneja el error, el programa puede continuar;
           o
en caso contrario, Python imprime el mensaje de error y sale:

>>> tomaNumero ()
Elige un n´mero: 17
          u
ErrorN´meroMalo: 17 es un mal n´mero
      u                        u

El mensaje de error incluye el tipo de excepci´n y la informaci´n adicional que
                                              o                o
usted 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        u



11.6.      Glosario
archivo: 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                                 e

ruta: Una secuencia de nombres de directorio que especifica la localizaci´n
                                                                        o
     exacta de un archivo.
11.6 Glosario                                                               129

archivo 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.
               o

operador 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.
                                     o

secuencia de formato: Una secuencia de caracteres que comienza con % e in-
     dica c´mo formatear un valor.
           o

encurtir: 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.
                                                          a

excepci´n: Un error que ocurre en tiempo de ejecuci´n.
       o                                           o

manejar: 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
Todo Sobre El Lenguaje Python
Cap´
   ıtulo 12

Clases y objetos

12.1.      Tipos compuestos definidos por el usuario
Una vez utilizados algunos de los tipos internos de Python, estamos listos para
crear un tipo definido por el usuario: el Punto.
Piense en el concepto de un punto matem´tico. En dos dimensiones, un punto es
                                        a
dos n´meros (coordenadas) que se tratan colectivamente como un solo objeto.
     u
En notaci´n matem´tica, los puntos suelen escribirse entre par´ntesis con una
          o         a                                           e
coma 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 el
origen.
Una forma natural de representar un punto en Python es con dos valores en
coma flotante. La cuesti´n es, entonces, c´mo agrupar esos dos valores en un
                        o                 o
objeto compuesto. La soluci´n r´pida y burda es utilizar una lista o tupla, y
                            o a
para algunas aplicaciones esa podr´ ser la mejor opci´n.
                                  ıa                 o
Una alternativa es que el usuario defina un nuevo tipo compuesto, tambi´n
                                                                      e
llamado una clase. Esta aproximaci´n exige un poco m´s de esfuerzo, pero
                                      o               a
tiene sus ventajas que pronto se har´n evidentes.
                                    a
Una definici´n de clase se parece a esto:
           o

class Punto:
  pass

Las 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 objetos

sint´cticas de la definici´n de clases son las mismas que para cualesquiera otras
    a                    o
sentencias compuestas. (ver la Secci´n 4.4).
                                      o
Esta definici´n crea una nueva clase llamada Punto. La sentencia pass no tiene
              o
efectos; s´lo es necesaria porque una sentencia compuesta debe tener algo en su
          o
cuerpo.
Al crear la clase Punto hemos creado un nuevo tipo, que tambi´n se llama Punto.
                                                             e
Los miembros de este tipo se llaman instancias del tipo u objetos. La creaci´n
                                                                             o
de una nueva instancia se llama instanciaci´n. Para instanciar un objeto Punto
                                           o
ejecutamos una funci´n que se llama (lo ha adivinado) Punto:
                      o

blanco = Punto()

A la variable blanco se le asigna una referencia a un nuevo objeto Punto. A
una funci´n como Punto que crea un objeto nuevo se le llama constructor.
         o



12.2.      Atributos
Podemos a˜adir nuevos datos a una instancia utilizando la notaci´n de punto:
         n                                                      o

>>> blanco.x = 3.0
>>> blanco.y = 4.0

Esta sintaxis es similar a la sintaxis para seleccionar una variable de un m´du-
                                                                            o
lo, como math.pi o string.uppercase. En este caso, sin embargo, estamos
seleccionando un dato de una instancia. Estos ´     ıtemes con nombre se llaman
atributos.
El diagrama de estados que sigue muestra el resultado de esas asignaciones:

                         blanco         x       3.0
                                        y       4.0

La variable blanco apunta a un objeto Punto, que contiene dos atributos. Cada
atributo apunta a un n´mero en coma flotante.
                      u
Podemos leer el valor de un atributo utilizando la misma sintaxis:

>>> print blanco.y
4.0
>>> x = blanco.x
>>> print x
3.0
12.3 Instancias como par´metro
                        a                                                    133

La expresi´n blanco.x significa, “ve al objeto al que apunta blanco y toma el
           o
valor de x”. En este caso, asignamos ese valor a una variable llamada x. No hay
conflicto entre la variable x y el atributo x. El prop´sito de la notaci´n de punto
                                                     o                 o
es identificar de forma inequ´ ıvoca a qu´ variable se refiere.
                                         e

Puede 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.y

La 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
                                                     a
objeto 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.
                                 u



12.3.      Instancias como par´metro
                              a
Puede usted pasar una instancia como par´metro de la forma habitual. Por
                                        a
ejemplo:

def imprimePunto(p):
  print ’(’ + str(p.x) + ’, ’ + str(p.y) + ’)’

imprimePunto acepta un punto como argumento y lo muestra en formato
est´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 objetos

12.4.        Mismidad
El significado de la palabra “mismo” parece totalmente claro hasta que uno se
para un poco a pensarlo, y entonces se da cuenta de que hay algo m´s de lo que
                                                                  a
supon´ıa.
Por ejemplo, si dice “Pepe y yo tenemos la misma moto”, lo que quiere decir es
que su moto y la de usted son de la misma marca y modelo, pero que son dos
motos distintas. Si dice “Pepe y yo tenemos la misma madre”, quiere decir que
su madre y la de usted son la misma persona1 . As´ que la idea de “identidad”
                                                  ı
es diferente seg´n el contexto.
                u
Cuando habla de objetos, hay una ambig¨edad parecida. Por ejemplo, si dos
                                         u
Puntos son el mismo, ¿significa que contienen los mismos datos (coordenadas)
o que son de verdad el mismo objeto?
Para averiguar si dos referencias se refieren al mismo objeto, utilice el operador
==. Por ejemplo:

>>>     p1 = Punto()
>>>     p1.x = 3
>>>     p1.y = 4
>>>     p2 = Punto()
>>>     p2.x = 3
>>>     p2.y = 4
>>>     p1 == p2
0

Aunque p1 y p2 contienen las mismas coordenadas, no son el mismo objeto. Si
asignamos p1 a p2, las dos variables son alias del mismo objeto:

>>>     p2 = p1
>>>     p1 == p2
1

Este tipo de igualdad se llama igualdad superficial porque s´lo compara las
                                                           o
referencias, pero no el contenido de los objetos.
Para comparar los contenidos de los objetos (igualdad profunda) podemos
escribir una funci´n llamada mismoPunto:
                  o

def 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
                                                                            a
diferentes para los diferentes tipos de identidad. “Misma moto” en este contexto ser´ “gleiche
                                                                                    ıa
Motorrad” y “misma madre” ser´ “selbe Mutter”.
                                   ıa
12.5 Rect´ngulos
         a                                                                135

Si 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)
1
Por supuesto, si las dos variables apuntan al mismo objeto mismoPunto devuelve
verdadero.



12.5.      Rect´ngulos
               a
Digamos 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                                               a
simplificar las cosas, supongamos que el rect´ngulo est´ orientado vertical u
                                            a         a
horizontalmente, nunca en diagonal.
Tenemos varias posibilidades: podemos se˜alar el centro del rect´ngulo (dos
                                            n                       a
coordenadas) y su tama˜o (anchura y altura); o podemos se˜alar una de las
                          n                                    n
esquinas y el tama˜o; o podemos se˜alar dos esquinas opuestas. Un modo con-
                   n                n
vencional es se˜alar la esquina superior izquierda del rect´ngulo y el tama˜o.
               n                                           a               n
De nuevo, definiremos una nueva clase:
class Rectangulo: # Prohibidos los acentos fuera de las cadenas!
  pass
Y la instanciaremos:
caja = Rectangulo()
caja.anchura = 100.0
caja.altura = 200.0
Este c´digo crea un nuevo objeto Rectangulo con dos atributos en coma flo-
       o
tante. ¡Para se˜alar la esquina superior izquierda podemos incrustar un objeto
               n
dentro de otro!
caja.esquina = Punto()
caja.esquina.x = 0.0;
caja.esquina.y = 0.0;
136                                                          Clases y objetos

El operador punto compone. La expresi´n caja.esquina.x significa “ve al ob-
                                          o
jeto al que se refiere caja y selecciona el atributo llamado esquina; entonces ve
a 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.0



12.6.      Instancias como valores de retorno
Las 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 p

Para llamar a esta funci´n, pase caja como argumento y asigne el resultado a
                        o
una variable:

>>> centro = encuentraCentro(caja)
>>> imprimePunto(centro)
(50.0, 100.0)



12.7.      Los objetos son mudables
Podemos cambiar el estado de un objeto efectuando una asignaci´n sobre uno
                                                               o
de sus atributos. Por ejemplo, para cambiar el tama˜o de un rect´ngulo sin
                                                     n           a
cambiar su posici´n, podemos cambiar los valores de anchura y altura:
                 o

caja.anchura = caja.anchura + 50
caja.altura = caja.altura + 100

Podemos encapsular este c´digo en un m´todo y generalizarlo para agrandar el
                          o           e
rect´ngulo en cualquier cantidad:
    a
12.8 Copiado                                                                137

def agrandaRect(caja, danchura, daltura) :
  caja.anchura = caja.anchura + danchura
  caja.altura = caja.altura + daltura

Las variables danchura y daltura indican cu´nto debe agrandarse el rect´ngulo
                                              a                           a
en cada direcci´n. Invocar este m´todo tiene el efecto de modificar el Rectangulo
               o                 e
que se pasa como argumento.
Por ejemplo, podemos crear un nuevo Rectangulo llamado bob y pas´rselo a
                                                                a
agrandaRect:

>>>   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                   a
Cualquier 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.
                     n



12.8.      Copiado
El uso de alias puede hacer que un programa sea dif´ de leer, porque los
                                                      ıcil
cambios hechos en un lugar pueden tener efectos inesperados en otro lugar. Es
dif´ estar al tanto de todas las variables a las que puede apuntar un objeto
   ıcil
dado.
Copiar un objeto es, muchas veces, una alternativa a la creaci´n de un alias. El
                                                              o
m´dulo copy contiene una funci´n llamada copy que puede duplicar cualquier
  o                            o
objeto:

>>>   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.      Glosario
clase: 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.
                 e

atributo: 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
Todo Sobre El Lenguaje Python
Cap´
   ıtulo 13

Clases y funciones

13.1.      Hora
Como otro ejemplo de un tipo definido por el usuario, definiremos una clase
llamada Hora que registra la hora del d´ La definici´n de la clase es como
                                       ıa.         o
sigue:
class Hora:
  pass
Podemos crear un nuevo objeto Hora y asignar atributos para contener las horas,
minutos y segundos:
hora = Hora()
hora.horas = 11
hora.minutos = 59
hora.segundos = 30
El 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 puras
En las pr´ximas secciones, escribiremos dos versiones de una funci´n llamada
         o                                                        o
sumaHora que calcule la suma de dos Horas. Mostrar´n dos tipos de funciones:
                                                    a
funciones 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 suma

La funci´n crea un nuevo objeto Hora, inicializa sus atributos y devuelve una
         o
referencia al nuevo objeto. A esto se le llama funci´n pura porque no modifica
                                                    o
ninguno de los objetos que se le pasan y no tiene efectos laterales, como mostrar
un valor o tomar una entrada del usuario.

Aqu´ tiene un ejemplo de c´mo usar esta funci´n. Crearemos dos objetos Hora:
    ı                     o                   o
horaActual, que contiene la hora actual, y horaPan, que contiene la cantidad
de tiempo que necesita un panadero para hacer pan. Luego usaremos sumaHora
para averiguar cu´ndo estar´ hecho el pan. Si a´n no ha terminado de escribir
                 a         a                    u
imprimeHora, 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                                                            143

La salida de este programa es 12:49:30, lo que es correcto. Por otra parte, hay
casos 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                                   u
segundos o minutos suma m´s que sesenta. Cuando ocurre eso, debemos “llevar”
                           a
los segundos sobrantes a la columna de los minutos o los minutos extras a la
columna de las horas.

He aqu´ una versi´n corregida de la funci´n:
      ı          o                       o

def 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 suma

Aunque esta funci´n es correcta, empieza a ser grande. M´s adelante sugeriremos
                 o                                      a
una aproximaci´n alternativa que nos dar´ un c´digo m´s corto.
               o                          a      o      a




13.3.      Modificadores
Hay veces en las que es util que una funci´n modifique uno o m´s de los objetos
                         ´                 o                    a
que recibe como par´metros. Normalmente, el llamante conserva una referencia
                    a
a los objetos que pasa, as´ que cualquier cambio que la funci´n haga ser´ visible
                           ı                                 o          a
para 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          u
escribir´ de forma natural como un modificador. Un esbozo r´pido de la funci´n
        ıa                                                a                o
podr´ ser ´ste:
     ıa    e
144                                                        Clases y funciones

def 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 + 1

La primera l´ınea realiza la operaci´n b´sica, las restantes tratan con los casos
                                    o a
especiales que vimos antes.
¿Es correcta esta funci´n? ¿Qu´ ocurre si el par´metro segundos es mucho
                         o        e               a
mayor que sesenta? En tal caso, no es suficiente con acarrear una vez; debemos
seguir haci´ndolo hasta que segundos sea menor que sesenta. Una soluci´n es
            e                                                            o
sustituir 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 + 1

Ahora 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.
                               o



13.4.      ¿Qu´ es mejor?
              e
Todo lo que se pueda hacer con modificadores puede hacerse tambi´n con fun-
                                                                  e
ciones puras. En realidad, algunos lenguajes de programaci´n s´lo permiten
                                                            o o
funciones 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                          145

los programas que usan modificadores. Sin embargo, a veces los modificadores
son 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.
              ı                               o
Este enfoque podr´ llamarse estilo funcional de programaci´n.
                    ıa                                            o



13.5.      Desarrollo de prototipos frente a planifi-
           caci´n
               o
En este cap´
           ıtulo mostramos una aproximaci´n al desarrollo de programas a la
                                              o
que llamamos desarrollo de prototipos. En cada caso, escribimos un esbozo
basto (o prototipo) que realizaba el c´lculo b´sico y luego lo probamos sobre
                                        a       a
unos cuantos casos, corrigiendo los fallos tal como los encontr´bamos.
                                                               a
Aunque este enfoque puede ser efecitvo, puede conducirnos a c´digo que es
                                                               o
innecesariamente complicado, ya que trata con muchos casos especiales, y poco
fiable, porque es dif´ saber si encontr´ todos los errores.
                    ıcil              o
Una alternativa es el desarrollo planificado, en el que una comprensi´n del
                                                                         o
problema en profundidad puede hacer la programaci´n mucho m´s f´cil. En este
                                                    o          a a
caso, el enfoque es que un objeto Hora es en realidad ¡un n´mero de tres d´
                                                           u              ıgitos
en base 60! El componente segundo es la “columna de unidades”, el componente
minuto es la “columna de las sesententas” y el componente hora es la “columna
de las tresmilseiscentenas”.
Cuando escribimos sumaHora e incremento, en realidad est´bamos haciendo
                                                            a
una suma en base 60, que es por lo que deb´
                                          ıamos acarrear de una columna a la
siguiente.
Esta observaci´n sugiere otro enfoque para el problema. Podemos convertir un
               o
objeto Hora en un simple n´mero y sacar provecho del hecho de que la m´quina
                           u                                            a
sabe la aritm´tica necesaria. La siguiente funci´n convierte un objeto Hora en
             e                                  o
un entero:

def convierteASegundos(t):
  minutos = t.horas * 60 + t.minutos
  segundos = minutos * 60 + t.segundos
  return segundos

Ahora, s´lo necesitamos una forma de convertir un entero en un objeto Hora:
        o

def 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 hora
Puede que tenga usted que pensar un poco para convencerse de que esta t´cni-
                                                                       e
ca para convertir de una base a otra es correcta. Suponiendo que est´ usted
                                                                    a
convencido, 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 a
mostrar que es correcta (suponiendo, como es habitual, que las funciones a las
que llama son correctas).

      Como ejercicio, reescriba incremento de la misma forma.



13.6.      Generalizaci´n
                       o
De alg´n modo, convertir de base 60 a base 10 y de vuelta es m´s dif´ que
      u                                                        a    ıcil
simplemente manejarse con las horas. La conversi´n de base es m´s abstracta;
                                                  o            a
nuestra intuici´n para tratar con las horas es mejor.
               o
Pero si tenemos la comprensi´n para tratar las horas como n´meros en base 60, y
                              o                              u
hacer la inversi´n de escribir las funciones de conversi´n (convierteASegundos
                o                                       o
y haceHora), obtenemos un programa que es m´s corto, m´s f´cil de leer y
                                                    a          a a
depurar y m´s fiable.
             a
Tambi´n es m´s f´cil a˜adir funcionalidades m´s tarde. Por ejemplo, imagine
       e       a a     n                        a
restar dos Horas para hallar el intervalo entre ellas. La aproximaci´n ingenua
                                                                     o
ser´ implementar la resta con acarreo. Con el uso de las funciones de conversi´n
   ıa                                                                         o
ser´ m´s f´cil y con mayor probabilidad, correcto.
   a a a
Ir´nicamente, a veces hacer un poblema m´s complejo (o m´s general) lo hace
  o                                       a               a
m´s f´cil (porque hay menos casos especiales y menos oportunidades de error).
  a a



13.7.      Algoritmos
Cuando escribe una soluci´n general para una clase de problemas, en contraste
                         o
con una soluci´n espec´
              o       ıfica a un problema concreto, ha escrito un algoritmo.
13.8 Glosario                                                                147

Mencionamos esta palabra antes pero no la definimos con precisi´n. No es f´cil
                                                              o          a
de definir, as´ que probaremos un par de enfoques.
             ı
Primero, piense en algo que no es un algoritmo. Cuando usted aprendi´ a mul-
                                                                      o
tiplicar n´meros de una cifra, probablemente memoriz´ la tabla de multiplicar.
          u                                           o
En efecto, memoriz´ 100 soluciones espec´
                   o                     ıficas. Ese tipo de conocimiento no es
algor´ıtmico.
Pero si usted era “harag´n” probablemente hizo trampa aprendiendo algunos
                          a
trucos. 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 una
soluci´n general para multiplicar cualquier n´mero de una cifra por 9. ¡Eso es
      o                                      u
un algoritmo!
De forma similar, las t´cnicas que aprendi´ para la suma y la resta con aca-
                        e                   o
rreo y la divisi´n larga son todas algoritmos. Una de las caracter´
                o                                                  ısticas de los
algoritmos es que no requieren inteligencia para llevarse a cabo. Son procesos
mec´nicos en los que cada paso sigue al anterior de acuerdo a un conjunto simple
    a
de reglas.
En nuestra opini´n, es un poco vergonzoso que los humanos pasen tanto tiempo
                 o
en 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                                      ıo
lectual 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. Entender
el 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
                                      o
de un algoritmo.


13.8.      Glosario
funci´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                ıa
modificador: 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                ıa
estilo funcional de programaci´n: Un estilo de programaci´n en el que la
                                  o                      o
      mayor´ de las funciones son puras.
           ıa
desarrollo de prototipos: Una forma de desarrollar programas empezando
     con un prototipo y prob´ndolo y mejor´ndolo gradualmente.
                            a             a
148                                                     Clases y funciones

desarrollo 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 14

Clases y m´todos
          e

14.1.      Caracter´
                   ısticas de la orientaci´n a objetos
                                          o
Python es un lenguaje de programaci´n orientado a objetos, lo que signi-
                                       o
fica que porporciona caracter´
                            ısticas que apoyan la programaci´n orientada
                                                            o
a objetos.
No es f´cil definir la programaci´n orientada a objetos, pero ya hemos visto
       a                        o
algunas 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.
                               u

Por ejemplo, la clase Hora definida en el Cap´ ıtulo 13 se corresponde con la forma
en 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  a
de un punto y un rect´ngulo.
                        a
Hasta ahora, no nos hemos aprovechado de las caracter´   ısticas que Python nos
ofrece para dar soporte a la programaci´n orientada a objetos. Hablando estric-
                                        o
tamente, 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
                                                                      e

la alternativa es m´s concisa y expresa con m´s precisi´n a la estructura del
                   a                         a         o
programa.
Por ejemplo, en el programa Hora no hay una conexi´n obvia entre la definici´n
                                                     o                     o
de la clase y las definiciones de las funciones que siguen. Observando bien, se
hace patente que todas esas funciones toman al menos un objeto Hora como
par´metro.
   a
Esta observaci´n es la que motiva los m´todos. Ya hemos visto varios m´todos,
              o                        e                                e
como keys y values, que se invocan sobre diccionarios. Cada m´todo est´ aso-
                                                                e         a
ciado con una clase y est´ pensado para invocarse sobre instancias de esa clase.
                         a
Los 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.
               o
En las pr´ximas secciones tomaremos las funciones de los cap´
          o                                                   ıtulos anteriores y
las transformaremos en m´todos. Esta transformaci´n es puramente mec´nica;
                          e                         o                      a
puede hacerla simplemente siguiendo una secuencia de pasos. Si se acostumbra
a convertir de una forma a la otra ser´ capaz de elegir la mejor forma de hacer
                                      a
lo que quiere.


14.2.      imprimeHora
En el Cap´
         ıtulo 13, definimos una clase llamada Hora y escribimos una fuci´n
                                                                        o
llamada imprimeHora, que deber´ ser parecida a esto:
                              ıa
class Hora:
  pass

def 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                                                            151

Para convertir imprimeHora en un m´todo, todo lo que necesitamos hacer es
                                      e
mover la definici´n de la funci´n al interior de la definici´n de la clase. F´
                o             o                           o                ıjese
en c´mo cambia el sangrado.
    o

class 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
                                                        e
del punto y el nombre del m´todo aparece tras el punto.
                           e
El 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.
                                          a
Por convenio, el primer par´metro de un m´todo se llama self. La raz´n de
                            a               e                       o
esto 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
                                          o
que 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-
             o
vocaci´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
                                           a
util. 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 escribir
funciones m´s vers´tiles, y hace m´s f´cil mantener y reutilizar c´digo.
             a      a               a a                            o



14.3.      Otro ejemplo
Vamos a convertir incremento (de la Secci´n 13.3) en un m´todo. Para aho-
                                           o                e
rrar espacio, dejaremos a un lado los m´todos ya definidos, pero usted deber´
                                       e                                   ıa
mantenerlos en su versi´n:
                       o

class 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 + 1

La transformaci´n es puramente mec´nica; hemos llevado la definici´n del m´to-
                 o                     a                            o        e
do al interior de la definici´n de la clase y hemos cambiado el nombre del primer
                            o
par´metro.
   a
Ahora podemos invocar incremento como un m´todo.
                                          e

horaActual.incremento(500)

De nuevo, el objeto sobre el que invocamos el m´todo se asigna al primer par´me-
                                               e                            a
tro, 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.
             e



14.4.      Un ejemplo m´s complicado
                       a
La funci´n despues es ligeramente m´s complicada porque opera sobre dos
         o                             a
objetos Hora, no s´lo sobre uno. S´lo podemos convertir uno de los par´metros
                   o              o                                   a
en self; el otro se queda como est´:
                                  a

class 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 0

Invocamos este m´todo sobre un objeto y pasamos el otro como argumento:
                e

if horaHecho.despues(horaActual):
  print "El pan estar´ hecho despu´s de empezar."
                     a            e

Casi puede leer la invocaci´n como una mezcla de ingl´s y espa˜ol: “Si la hora-
                           o                         e        n
hecho es depu´s de la hora-actual, entonces...”
              e



14.5.      Argumentos opcionales
Hemos visto funciones internas que toman un n´mero variable de argumentos.
                                             u
Por 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
                                                                  o
para que haga lo mismo que string.find.

Esta es la versi´n original de la Secci´n 7.7:
                o                      o

def encuentra(cad, c):
  indice = 0
  while indice < len(cad):
    if str[indice] == c:
      return indice
    indice = indice + 1
  return -1

Esta es la versi´n aumentada y mejorada:
                o

def 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
                                                                     e

El tercer par´metro, comienzo, es opcional porque se proporciona un valor por
             a
omisi´n, 0. Si invocamos encuentra s´lo con dos argumentos, utilizamos el valor
     o                              o
por omisi´n y comenzamos por el principio de la cadena:
          o
>>> encuentra("arriba", "r")
1
Si 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                    o
El m´todo de inicializaci´n es un m´todo especial que se invoca al crear un
      e                   o          e
objeto. El nombre de este m´todo es init (dos guiones bajos, seguidos de
                            e
init y dos guiones bajos m´s). Un m´todo de inicializaci´n para la clase Hora
                          a        e                    o
es as´
     ı:
class Hora:
  def __init__(self, horas=0, minutos=0, segundos=0):
    self.horas = horas
    self.minutos = minutos
    self.segundos = segundos
No hay conflicto entre el atributo self.horas y el par´metro horas. la notaci´n
                                                     a                      o
de punto especifica a qu´ variable nos referimos.
                        e
Cuando invocamos el constructor Hora, los argumentos que damos se pasan a
init:
>>> horaActual = Hora(9, 14, 30)
>>> horaActual.imprimeHora()
>>> 9:14:30
14.7 Revisi´n de los Puntos
           o                                                                155

Como los par´metros son opcionales, podemos omitirlos:
            a

>>> horaActual = Hora()
>>> horaActual.imprimeHora()
>>> 0:0:0

O dar s´lo el primer par´metro:
       o                a

>>> horaActual = Hora (9)
>>> horaActual.imprimeHora()
>>> 9:0:0

O los dos primeros par´metros:
                      a

>>> horaActual = Hora (9, 14)
>>> horaActual.imprimeHora()
>>> 9:14:0

Finalmente, podemos dar un subconjunto de los par´metros nombr´ndolos ex-
                                                 a            a
plicitamente:

>>> horaActual = Hora(segundos = 30, horas = 9)
>>> horaActual.imprimeHora()
>>> 9:0:30



14.7.      Revisi´n de los Puntos
                 o
Vamos a reescribir la clase Punto de la Secci´n 12.1 con un estilo m´s orientado
                                             o                      a
a 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                                   a
nales; el valor por omisi´n de cada par´metro es 0.
                         o             a
El siguiente m´todo, str , devuelve una representaci´n en forma de cadena
              e                                       o
de un objeto Punto. Si una clase ofrece un m´todo llamado str , se impone
                                            e
al 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.
                o


14.8.      Sobrecarga de operadores
Algunos lenguajes hacen posible cambiar la definici´n de los operadores internos
                                                  o
cuando se aplican a tipos definidos por el usuario. Esta caracter´ıstica se llama
sobrecarga de operadores. Es especialmente util cuando definimos nuevos
                                                 ´
tipos matem´ticos.
            a
Por ejemplo, para suplantar al operador de suma + necesitamos proporcionar
un m´todo llamado add :
     e
class 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
                                a
m´todo. El segundo par´metro se llama convenientemente otro para distinguirlo
   e                   a
del mismo (self). Para sumar dos Puntos, creamos y devolvemos un nuevo
Punto que contiene la suma de las coordenadas x y la suma de las coordenadas
y.
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                                             157

La expresi´n p1 + p2 equivale a p1. add (p2), pero es obviamente m´s ele-
          o                                                       a
gante.

     Como ejercicio, a˜ada un m´todo sub (self, otro) que sobre-
                       n          e
     cargue el operador resta y pru´belo.
                                   e

Hay varias formas de sobrecargar el comportamiento del operador multiplica-
ci´n: definiendo un m´todo llamado mul , o rmul , o ambos.
  o                 e
Si el operando a la izquierda de * es un Punto, Python invoca a mul , lo
que presupone que el otro operando es tambi´n un Punto. Calcula el producto
                                           e
interno de dos puntos, definido seg´n las reglas del ´lgebra lineal:
                                   u                a
def __mul__(self, otro):
  return self.x * otro.x + self.y * otro.y
Si el operando a la izquierda de * es un tipo primitivo y el operando de la
derecha es un Punto, Python invca a rmul , lo que realiza una multiplicaci´n
                                                                          o
escalar:
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-
                                                         u
nadas originales. Si otro es un tipo que no se puede multiplicar por un n´mero
                                                                         u
en coma flotante, entonces rmul causar´ un error.
                                           a
Este ejemplo muestra ambos tipos de multiplicaci´n:
                                                o
>>> p1 = Punto(3, 4)
>>> p2 = Punto(5, 7)
>>> print p1 * p2
43
>>> print 2 * p2
(10, 14)
¿Qu´ ocurre si intentamos evaluar p2 * 2? Como el primer par´metro es un
    e                                                       a
Punto, 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 lo
consigue porque un entero no tiene atributos:
>>> print p2 * 2
AttributeError: ’int’ object has no attribute ’x’
Desgraciadamente, el mensaje de error es un poco opaco. Este ejemplo muestra
algunas de las difucultades de la programaci´n orientada a objetos. A veces es
                                            o
dif´ averiguar simplemente qu´ c´digo se est´ ejecutando.
   ıcil                         e o           a
158                                                        Clases y m´todos
                                                                     e

Para ver un ejemplo m´s completo de sobrecarga de operadores, vaya al Ap´ndi-
                     a                                                  e
ce B.


14.9.      Polimorfismo
La mayor´ de los m´todos que hemos escrito funcionan s´lo para un tipo es-
           ıa        e                                    o
pec´ıfico. Cuando usted crea un nuevo objeto, escribe m´todos que operan sobre
                                                      e
ese tipo.
Pero hay ciertas operaciones que querr´ aplicar a muchos tipos, como las opera-
                                        a
ciones aritm´ticas de las secciones anteriores. Si muchos tipos admiten el mismo
            e
conjunto de operaciones, puede escribir funciones que trabajen sobre cualquiera
de esos tipos.
Por ejemplo, la operaci´n multisuma (com´n en ´lgebra lineal) toma tres
                         o                    u     a
par´metros; multiplica los dos primeros y luego suma el tercero. Podemos escri-
    a
birla en Python as´
                  ı:
def multisuma (x, y, z):
  return x * y + z
Este m´todo trabajar´ con cualquier valor de x e y que se pueda multiplicar y
       e              a
con cualquier valor de z que se pueda sumar al producto.
Podemos invocarlo con valores num´ricos:
                                 e
>>> multisuma (3, 2, 1)
7
O con Puntos:
>>> p1 = Punto(3, 4)
>>> p2 = Punto(5, 7)
>>> print multisuma (2, p1, p2)
(11, 15)
>>> print multisuma (p1, p2, 1)
44
En el primer caso, el Punto se multiplica por un escalar y luego se suma a
otro Punto. En el segundo caso, el producto interior produce un valor num´rico,
                                                                         e
as´ que el tercer par´metro tambi´n debe ser un valor num´rico.
  ı                  a            e                        e
Una funci´n como ´sta que puede tomar par´metros con diferentes tipos se
         o        e                      a
llama polim´rfica.
           o
Como un ejemplo m´s, observe el m´todo delDerechoYDelReves, que imprime
                     a               e
dos veces una lista, hacia adelante y hacia atr´s:
                                               a
14.9 Polimorfismo                                                            159

def 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
           e
antes de darle la vuelta. As´ este m´todo no modifica la lista que recibe como
                            ı,      e
par´metro.
   a

He 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.
                                                e

Para determinar si una funci´n se puede aplicar a un nuevo tipo, aplicamos la
                            o
regla 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.
                                     o

Las operaciones del m´todo incluyen copy, reverse y print.
                     e

copy trabaja sobre cualquier objeto, y ya hemos escrito un m´todo str para
                                                            e
los Puntos, as´ que todo lo que necesitamos es un m´todo reverse en la clase
              ı                                      e
Punto:

def reverse(self):
  self.x , self.y = self.y, self.x

Ahora 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 que
una funci´n que hab´ escrito se puede aplicar a un tipo para el que nunca la
         o           ıa
hab´ planeado.
   ıa
160                                                        Clases y m´todos
                                                                     e

14.10.       Glosario
lenguaje 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.
       e

m´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.
                                     e

multiplicaci´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.
                                                                   e

polim´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 15

Conjuntos de objetos

15.1.      Composici´n
                    o
Hasta ahora, ya ha visto varios ejemplos de composici´n. Uno de los primeros
                                                      o
ejemplos fue el uso de la llamada a un m´todo como parte de una expresi´n.
                                         e                                  o
Otro ejemplo es la estructura anidada de las sentencias; se puede escribir una
sentencia 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                                                       ıa
sorprender que pueda crear listas de objetos. Tambi´n puede crear objetos que
                                                     e
contengan 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 Carta
Si no est´ usted familiarizado con los naipes de juego comunes, puede ser un
          a
buen momento para que consiga un mazo, si no este cap´  ıtulo puede que no tenga
mucho sentido. Hay cincuenta y dos naipes en una baraja inglesa, cada uno de
los cuales pertenece a un palo y tiene un valor; hay cuatro palos diferentes y
trece valores. Los palos son Picas, Corazones, Diamantes, y Tr´boles (en el orden
                                                               e
descendente seg´n el bridge). Los valores son As, 2, 3, 4, 5, 6, 7, 8, 9, 10, Sota,
                 u
Reina, y Rey. Dependiendo del tipo de juego que se juegue, el valor del As puede
ser mayor al Rey o inferior al 2.
162                                                    Conjuntos de objetos

Si queremos definir un nuevo objeto para representar un naipe, es obvio qu´ atri-
                                                                            e
butos deber´ tener: valor y palo. Lo que no es tan obvio es el tipo que se debe
             ıa
dar a los atributos. Una posibilidad es usar cadenas de caracteres que contengan
palabras como "Picas" para los palos y "Reina" para los valores. Un problema
de esta implementaci´n es que no ser´ f´cil comparar naipes para ver cu´l tiene
                      o               a a                                 a
mayor valor o palo.

Una alternativa es usar n´meros enteros para codificar los valores y palos. Con
                          u
el t´rmino “codificar” no queremos significar lo que algunas personas pueden
    e
pensar, acerca de cifrar o traducir a un c´digo secreto. Lo que un programador
                                          o
entiende por “codificar” es “definir una correspondencia entre una secuencia de
n´meros y los elementos que se desea representar”. Por ejemplo:
 u

 Picas        →     3
 Corazones    →     2
 Diamantes    →     1
 Tr´boles
   e          →     0
Esta correspondencia tiene una caracter´ ıstica obvia: los palos corresponden a
n´meros enteros en orden, o sea que podemos comparar los palos al comparar
  u
los n´meros. La asociaci´n de los valores es bastante obvia; cada uno de los
     u                   o
valores num´ricos se asocia con el entero correspondiente, y para las figuras:
           e

 Sota     →    11
 Reina    →    12
 Rey      →    13
Estamos usando una notaci´n matem´tica para estas asociaciones por una
                             o          a
raz´n: no son parte del programa Python. Son parte del dise˜o del progra-
   o                                                            n
ma, pero nunca aparecen expl´  ıcitamente en el c´digo fuente. La definici´n de
                                                 o                       o
clase para el tipo Carta se parecer´ a:
                                    a

class Carta:
  def __init__(self, palo=0, valor=0):
    self.palo = palo
    self.valor = valor

Como acostumbramos, proporcionaremos un m´todo de inicializaci´n que toma
                                          e                   o
un par´metro opcional para cada atributo.
      a

Para crear un objeto que representa el 3 de Tr´boles, usaremos la instrucci´n:
                                              e                            o

tresDeTreboles = 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                             163

15.3.      Atributos de clase y el m´todo
                                    e                             str
Para poder imprimir los objetos Carta de una manera f´cil de leer para las
                                                         a
personas, vamos a establecer una correspondencia entre los c´digos enteros y
                                                             o
las palabras. Una manera natural de hacer esto es con listas de cadenas de
caracteres. Asignaremos estas listas dentro de atributos de clase al principio
de la definici´n de clase:
             o

class 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
                                                  e
desde cualquiera de los m´todos de la clase.
                         e

Dentro de str , podemos usar listaDePalos y listaDeValores para aso-
ciar los valores num´ricos de palo y valor con cadenas de caracteres. Por
                     e
ejemplo, la expresi´n self.listaDePalos[self.palo] significa “usa el atribu-
                   o
to palo del objeto self como un ´
                                ındice dentro del atributo de clase denominado
listaDePalos, y selecciona la cadena apropiada”.

El motivo del ‘‘nada" en el primer elemento de listaDeValores es para relleno
del 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 este
primer elemento. Podr´ ıamos haber comenzado en 0 como es usual, pero es menos
confuso 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 carta1
Sota de Diamantes

Los atributos de clase como listaDePalos son compartidos por todos los ob-
jetos de tipo Carta. La ventaja de esto es que podemos usar cualquier objeto
Carta para acceder a los atributos de clase:
164                                                     Conjuntos de objetos

>>> carta2 = Carta(1, 3)
>>> print carta2
3 de Diamantes
>>> print carta2.listaDePalos[1]
Diamantes

La desventaja es que si modificamos un atributo de clase, afectaremos a cada
instancia de la clase. Por ejemplo, si decidimos que “Sota de Diamantes” en
realidad deber´ llamarse “Sota de Ballenas Bailarinas”, podr´
               ıa                                             ıamos hacer lo
siguiente:

>>> carta1.listaDePalos[1] = "Ballenas Bailarinas"
>>> print carta1
Sota de Ballenas Bailarinas

El problema es que todos los Diamantes se transformar´n en Ballenas Bailarinas:
                                                     a

>>> print carta2
3 de Ballenas Bailarinas

En general no es una buena idea modificar los atributos de clase.



15.4.      Comparaci´n de naipes
                    o
Para los tipos primitivos, existen operadores condicionales (< , >, ==, etc.) que
comparan valores y determinan cuando uno es mayor, menor, o igual a otro.
Para los tipos definidos por el usuario, podemos sustituir el comportamiento
de los operadores internos si proporcionamos un m´todo llamado cmp . Por
                                                     e
convenci´n, cmp toma dos par´metros, self y otro, y retorna 1 si el primer
         o                         a
objeto 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
                  a
comparar dos elementos cualesquiera y decir cu´l es el mayor. Por ejemplo, los
                                                 a
n´meros enteros y los n´meros en coma flotante tienen un orden completo. Algu-
 u                     u
nos conjuntos no tienen orden, o sea, que no existe ninguna manera significativa
de decir que un elemento es mayor a otro. Por ejemplo, las frutas no tienen
orden, lo que explica por qu´ no se pueden comparar peras con manzanas.
                            e

El conjunto de los naipes tiene un orden parcial, lo que significa que algunas
veces se pueden comparar los naipes, y otras veces no. Por ejemplo, usted sabe
que el 3 de Tr´boles es mayor que el 2 de Tr´boles y el 3 de Diamantes es
               e                               e
mayor que el 3 de Tr´boles. Pero, ¿cu´l es mejor?, ¿el 3 de Tr´boles o el 2 de
                     e                a                        e
Diamantes?. Uno tiene mayor valor, pero el otro tiene mayor palo.
15.5 Mazos de naipes                                                        165

A los fines de hacer que los naipes sean comparables, se debe decidir qu´ es m´s
                                                                        e    a
importante: valor o palo. Para no mentir, la selecci´n es arbitraria. Como algo
                                                    o
hay que elegir, diremos que el palo es m´s importante, porque un mazo nuevo
                                         a
viene ordenado con todos los Tr´boles primero, luego con todos los Diamantes,
                                e
y 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 0

En 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 naipes
Ahora que ya tenemos los objetos para representar las Cartas, el pr´ximo paso
                                                                   o
l´gico es definir una clase para representar un Mazo. Por supuesto, un mazo
 o
est´ compuesto de naipes, as´ que cada objeto Mazo contendr´ una lista de
   a                         ı                                 a
naipes como atributo.
A continuaci´n se muestra una definici´n para la clase Mazo. El m´todo de
              o                            o                             e
inicializaci´n crea el atributo cartas y genera el conjunto est´ndar de cincuenta
            o                                                  a
y 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 a
exterior 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 objetos

itera trece veces, la cantidad total de veces que se ejecuta el cuerpo interior es
cincuenta y dos (trece por cuatro). Cada iteraci´n crea una nueva instancia de
                                                 o
Carta 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.
    e


15.6.      Impresi´n del mazo de naipes
                  o
Como es usual, cuando definimos un nuevo tipo de objeto queremos un m´todo
                                                                        e
que imprima el contenido del objeto. Para imprimir un Mazo, recorremos la lista
e imprimimos cada Carta:
class Mazo:
  ...
  def muestraMazo(self):
    for carta in self.cartas:
      print carta
Desde ahora en adelante, los puntos suspensivos (...) indicar´n que hemos
                                                             a
omitido los otros m´todos en la clase.
                   e
En 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             a
lugar 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
          o
pueden 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                                        o
cadena de caracteres de un Mazo. Para darle un toque especial, acomoda los
naipes en una cascada, de tal manera que cada naipe est´ sangrado un espacio
                                                       a
m´s que el precedente.
  a
class Mazo:
  ...
  def __str__(self):
    s = ""
    for i in range(len(self.cartas)):
      s = s + " "*i + str(self.cartas[i]) + "n"
    return s
Este ejemplo demuestra varias caracter´
                                      ısticas. Primero, en lugar de recorrer
self.cartas y asignar cada naipe a una variable, usamos i como variable de
bucle e ´
        ındice de la lista de naipes.
Segundo, utilizamos el operador de multiplicaci´n de cadenas de caracteres para
                                               o
sangrar cada naipe un espacio m´s que el anterior. La expresi´n *i prooprciona
                                a                            o
una cantidad de espacios igual al valor actual de i.
15.7 Barajar el mazo                                                         167

Tercero, en lugar de usar la instrucci´n print para imprimir los naipes, utiliza-
                                      o
mos la funci´n str. El pasar un objeto como argumento a str es equivalente a
             o
invocar el m´todo str sobre dicho objeto.
             e

Finalmente, usamos la variable s como acumulador. Inicialmente, s es una
cadena de caracteres vac´ En cada pasada a trav´s del bucle, se genera una
                        ıa.                         e
nueva cadena de caracteres que se concatena con el viejo valor de s para obtener
el nuevo valor. Cuando el bucle termina, s contiene la representaci´n completa
                                                                    o
en formato de cadena de caracteres del Mazo, la cual se ve como a continuaci´no
se presenta:

>>> mazo = Mazo()
>>> print mazo
As 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 Diamantes

Y as´ sucesivamente. A´n cuando los resultados aparecen en 52 renglones, se
     ı                 u
trata de s´lo una unica larga cadena de caracteres que contiene los saltos de
          o       ´
l´
 ınea.



15.7.      Barajar el mazo
Si un mazo est´ perfectamente barajado, cualquier naipe tiene la misma proba-
               a
bilidad de aparecer en cualquier posici´n del mazo, y cualquier lugar en el mazo
                                       o
tiene la misma probabilidad de contener cualquier naipe.

Para mezclar el mazo, utilizaremos la funci´n randrange del m´dulo random. Es-
                                           o                 o
ta funci´n toma dos enteros como argumentos a y b, y elige un n´mero entero en
        o                                                      u
168                                                     Conjuntos de objetos

forma 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 argumento
y de esa manera tendremos garantizado un ´    ındice legal dentro de la lista. Por
ejemplo, 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 intercambiar
cada una con otra elegida al azar. Es posible que el naipe se intercambie consigo
mismo, pero no es un problema. De hecho, si eliminamos esa posibilidad, el
orden de los naipes no ser´ completamente al azar:
                          a

class 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, obtenemos
la longitud real de la lista y la almacenamos en nCartas.

Para cada naipe del mazo, seleccionamos un naipe al azar entre aquellos que no
han sido intercambiados a´n. Luego intercambiamos el naipe actual (i) con el
                          u
naipe seleccionado (j). Para intercambiar los naipes usaremos la asignaci´n de
                                                                         o
tuplas, como se describe en la Secci´n 9.2:
                                    o

self.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.
        o



15.8.      Eliminaci´n y reparto de los naipes
                    o
Otro 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
                   a
en el mazo, y falso (0) si no estaba:
15.9 Glosario                                                               169

class Mazo:
  ...
  def eliminaCarta(self, carta):
    if carta in self.cartas:
      self.cartas.remove(carta)
      return 1
    else:
      return 0

El operador in retorna verdadero si el primer operando est´ en el segundo, el
                                                             a
cual debe ser una lista o tupla. Si el primer operando es un objeto, Python usa
el m´todo cmp del objeto para determinar la igualdad entre los elementos de
     e
la lista. Como el cmp en la clase Carta verifica la igualdad en profundidad,
el m´todo eliminaCarta tambi´n verifica igualdad en profundidad.
      e                          e
Para repartir los naipes, queremos eliminar y devolver el naipe que ocupa la
posici´n superior en el mazo. El m´todo pop de las listas proporciona una manera
      o                           e
conveniente 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                                               o
estaVacio, la cual devuelve verdadero si el mazo no contiene ning´n naipe:
                                                                 u

class Deck:
  ...
  def estaVacio(self):
    return (len(self.cartas) == 0)



15.9.      Glosario
codificar: 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 objetos

acumulador: 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 16

Herencia

16.1.      Herencia
La caracter´
           ıstica de un lenguaje que m´s se asocia con la programaci´n orientada
                                      a                             o
a objetos es la herencia. La herencia es la capacidad de definir una nueva clase
que es una versi´n modificada de otra ya existente.
                 o

La principal ventaja de esta caracter´ıstica es que se pueden agregar nuevos m´to-
                                                                              e
dos a una clase sin modificar la clase existente. Se denomina “herencia” porque
la nueva clase hereda todos los m´todos de la clase existente. Si extendemos
                                     e
esta mat´fora, a la clase existente a veces se la denomina clase padre. La nueva
         e
clase puede denominarse clase hija, o tambi´n “subclase”.
                                                e

La herencia es una caracter´ıstica poderosa. Ciertos programas que ser´ compli-
                                                                      ıan
cados 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        o
adaptar 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 a

Por 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        o
su definici´n. El c´digo relevante puede estar diseminado por varios m´dulos.
          o       o                                                       o
Adem´s, muchas de las cosas que se hacen mediante el uso de la herencia, se pue-
      a
den lograr de forma igualmente (incluso m´s) elegante sin ella. Si la estructura
                                          a
general del problema no nos gu´ hacia la herencia, dicho estilo de programaci´n
                              ıa                                               o
puede hacer m´s mal que bien.
               a
172                                                                   Herencia

En este cap´
           ıtulo demostraremos el uso de la herencia como parte de un programa
que juega a las cartas a la “Mona”. Una de nuestras metas ser´ que el c´digo
                                                                a         o
que escribamos se pueda reutilizar para implementar otros juegos de naipes.



16.2.      Una mano de cartas
Para 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
                                                           a
conjunto de naipes, y ambos requieren de operaciones tales como agregar y
eliminar una carta. Adem´s, necesitaremos la capacidad de mezclar tanto un
                           a
mazo como una mano de cartas.
Una mano es diferente de un mazo en ciertos aspectos. Seg´n el juego al que se
                                                            u
est´ jugando, podemos querer realizar ciertas operaciones sobre una mano que
   e
no tienen sentido sobre un mazo. Por ejemplo, en el p´ker queremos clasificar
                                                       o
una mano (straight (consecutiva), flush (de un solo palo), etc.) y compararla con
otra. 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,
            o
entonces tendr´ todos los m´todos de Mazo y le podremos agregar otros m´todos
              a             e                                           e
nuevos.
En la definici´n de clase, el nombre de la clase padre aparece entre par´ntesis:
             o                                                         e

class Mano(Mazo):
  pass

Esta 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 y
cartas. La cadena de caracteres nombre identifica a esta mano, probablemente
mediante el nombre del jugador que la sostiene. El nombre es un par´metro
                                                                        a
opcional 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 = nombre

Casi 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
                o
de Mazo. Pero deberemos escribir agregaCarta:
16.3 El reparto de los naipes                                                 173

class Mano(Mazo):
  ...
  def agregaCarta(self,carta) :
    self.cartas.append(carta)
De nuevo, los puntos suspensivos indican que hemos omitido los otrs m´todos.
                                                                        e
El m´todo de lista append agrega la nueva carta al final de la lista de cartas.
    e


16.3.      El reparto de los naipes
Ahora que ya tenemos la clase Mano, queremos repartir las cartas del Mazo en
manos. No es claramente obvio si este m´todo debe ir en la clase Mano o en la
                                       e
clase Mazo, pero como opera sobre un mazo unico y (posiblemente) sobre varias
                                          ´
manos, es m´s natural ponerlo en el Mazo.
             a
repartir debe ser bastante general, pues los diferentes juegos tienen distintos
requerimentos. Puede que necesitemos repartir todo el mazo de una vez, o que
agreguemos una carta a cada mano.
repartir toma dos par´metros, una lista (o tupla) de manos y la cantidad total
                        a
de naipes a repartir. Si no hay suficientes cartas en el mazo, el m´todo reparte
                                                                  e
todas 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 mano
El segundo par´metro, nCartas es opcional; el valor por omisi´n es un n´mero
              a                                               o          u
muy grande, lo cual es lo mismo que decir que se repartir´n todos los naipes del
                                                         a
mazo.
La variable de bucle i va desde 0 hasta nCartas-1. A cada paso a trav´s del
                                                                         e
bucle, se elimina una carta del mazo mediante el m´todo de lista pop, que quita
                                                  e
y 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 manos
en la lista, la expresi´n i % nManos salta hacia el comienzo de la lista (el ´
                       o                                                     ındice
es 0).
174                                                                 Herencia

16.4.      Mostremos la mano
Para mostrar el contenido de una mano, podemos sacar partido de la existencia
de 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 mano
La mano de hugo contiene
2 de Picas
 3 de Picas
  4 de Picas
   As de Corazones
    9 de Tr´boles
            e

No es una gran mano, pero tiene lo necesario como para disponer de una escalera
de color.

Aunque es conveniente usar la herencia de los m´todos existentes, existe infor-
                                               e
maci´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
                                           e
la 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 mano
est´ 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
                                                                           o
como cadena de caracteres del Mazo, que se obtiene llamando al m´todo str
                                                                e
de la clase Mazo sobre la instancia self.

Puede parecer extra˜o que enviemos a self, que se refiere a la Mano actual,
                   n
como argumento de un m´todo de la clase Mazo, hasta que nos damos cuenta
                         e
de que una Mano es un tipo de Mazo. Los objetos Mano pueden hacer cualquier
16.5 La clase JuegoDeCartas                                                 175

cosa que pueda hacer un objeto Mazo, y por ello es legal que pasemos una Mano
a un m´todo de Mazo.
       e
En general, siempre es legal usar una instancia de una subclase en el lugar de
una instancia de una clase padre.



16.5.      La clase JuegoDeCartas
La clase JuegoDeCartas asume la responsabilidad sobre algunas obligaciones
b´sicas comunes a todos los juegos, tales como la creaci´n del mazo y la mezcla
 a                                                      o
de 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                      o
actividad computacional significativa, m´s all´ de la inicializaci´n de atributos.
                                       a     a                   o
Para implementar juegos espec´   ıficos, debemos heredar de JuegoDeCartas y
agregar las caracter´
                    ısticas del nuevo juego. Como ejemplo, escribiremos una
simulaci´n para La Mona.
        o
La 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,
                                         a
el 4 de Tr´boles se empareja con el 4 de Picas porque ambos palos son negros.
          e
La Sota de Corazones se empareja con la Sota de Diamantes porque ambos son
rojos.
Para iniciar el juego, se elimina la Reina de Tr´boles del mazo, de manera que
                                                e
la Reina de Picas no tiene con qui´n emparejarse. Las cincuenta y una cartas
                                     e
restantes 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,
                                a
cada jugador toma una carta (sin mirarla) del vecino m´s cercano de la izquierda
                                                       a
que a´n tiene cartas. Si la carta elegida concuerda con una de la mano del
      u
jugador, 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 lo
que queda s´lo la Reina de Picas en la mano del perdedor.
            o
En nuestra simulaci´n inform´tica del juego, la computadora juega todas las
                    o         a
manos. 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                                                                 Herencia

que su vecino la tome, por ejemplo mostr´ndola prominentemente o al contrario,
                                         a
errando al intentar mostrarla abiertamente, o incluso puede fallar al tratar de
errar en su intento de mostrarla prominentemente. La computadora simplemente
toma una carta al azar de su vecino.



16.6.      La clase ManoDeLaMona
Una mano para jugar a La Mona requiere ciertas capacidades que est´n m´s
                                                                  a   a
all´ de las que posee una Mano. Definiremos una nueva clase ManoDeLaMona,
   a
que hereda de Mano y nos proporciona un m´todo adicional denominado
                                             e
eliminaCoincidencias:

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 cant

Comenzamos por hacer una copia de la lista de las cartas, de tal manera que
podamos recorrer la copia mientras vamos quitando cartas de la lista original.
Como self.cartas se modifica en el bucle, no vamos a querer usarla para
controlar el recorrido. ¡Python puede quedar realmente confundido si se recorre
una lista que est´ cambiando!
                 a

Para cada carta de la mano, averiguamos cu´l es la carta que concordar´ con
                                            a                            a
ella y la buscamos. La carta que concuerda tiene el mismo valor y el otro palo
del mismo color. La expresi´n 3 - carta.palo transforma un Tr´bol (palo 0)
                           o                                     e
en una Pica (palo 3) y un Diamante (palo 1) en un Coraz´n (palo 2). Verifique
                                                        o
por su cuenta que las operaciones opuestas tambi´n funcionan. Si la carta que
                                                 e
concuerda est´ en la mano, ambas se eliminan.
              a

El siguiente ejemplo demuestra el uso de eliminaCoincidencias:

>>> juego = JuegoDeCartas()
>>> mano = ManoDeLaMona("hugo")
>>> juego.mazo.repartir([mano], 13)
16.7 La clase JuegoDeLaMona                                              177

>>> print mano
La mano de hugo contiene
As 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
                                 e
Mano hugo: 8 de Picas con 8 de Tr´boles
                                 e
Mano hugo: 10 de Diamantes con 10 de Corazones
Debe usted notar que no existe un m´todo
                                   e       init   para la clase ManoDeLaMona.
Lo heredamos de Mano.


16.7.     La clase JuegoDeLaMona
Ahora 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
                                          e
toma una lista de jugadores como par´metro.
                                     a
Como el m´todo init se hereda de JuegoDeCartas, el nuevo objeto
          e
JuegoDeLaMona 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-
                                                               e
parados. eliminaTodasLasCoincidencias recorre la lista de manos y llama a
eliminaCoincidencias 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 cada
mano y devuelve el total.
Cuando la cantidad total de coincidencias alcanza a las veinticinco significa que
se han eliminado cincuenta cartas de las manos, lo que es lo mismo que decir
que s´lo queda una carta y el juego ha terminado.
     o
La variable turno recuerda el turno de cu´l jugador se est´ jugando. Comienza
                                         a                a
en cero y se incrementa en uno cada vez; cuando alcanza el valor cantManos, el
operador de m´dulo lo hace volver a cero.
               o
16.7 La clase JuegoDeLaMona                                                 179

El m´todo jugarUnTurno toma un par´metro que indica de qui´n es el turno.
     e                                   a                        e
El valor de retorno es la cantidad de concordancias que se han realizado durante
ese 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 cant
Si 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´nu
tiene 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
                                                                              o
del siguiente jugador sea al azar.
El m´todo encuentraVecino comienza con el jugador que est´ inmediatamante
     e                                                       a
a la izquierda y contin´a alrededor del c´
                       u                 ırculo hasta que encuentra un jugador
que a´n tiene cartas.
      u
class 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 vecino
Si por cualquier motivo encuentraVecino llegara a dar la vuelta completa al
c´
 ırculo sin encontrar cartas, devolver´ None y eso causar´ un error en alguna
                                      ıa                 ıa
otra parte del programa. Afortunadamante podemos probar que eso no va a
suceder nunca (siempre y cuando se detecte correctamente el final del juego).
                                          ´
Hemos omitido el m´todo muestraManos. Ese puede escribirlo usted mismo.
                     e
La 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                                                                  Herencia

jugadores. Con este mazo m´s peque˜o, el juego termina tras siete coincidencias,
                          a       n
en lugar de veinticinco.

>>> import cartas
>>> juego = cartas.JuegoDeLaMona()
>>> juego.jugar(["Allen","Jeff","Chris"])
----- Se han repartido las cartas.
Mano Allen contiene
Rey de Corazones
 Sota de Tr´boles
           e
  Reina de Picas
   Rey de Picas
    10 de Diamantes

Mano Jeff contiene
Reina de Corazones
 Sota de Picas
  Sota de Corazones
   Rey de Diamantes
    Reina de Diamantes

Mano Chris contiene
Sota de Diamantes
 Rey de Tr´boles
          e
  10 de Picas
   10 de Corazones
    10 de Tr´boles
            e

Mano Jeff: Reina de Corazones con Reina de Diamantes
Mano Chris: 10 de Picas con 10 de Tr´boles
                                    e
----- Se eliminaron las coincidencias, el juego comienza.
Mano Allen contiene
Rey de Corazones
 Sota de Tr´boles
           e
  Reina de Picas
   Rey de Picas
    10 de Diamantes

Mano Jeff contiene
Sota de Picas
 Sota de Corazones
  Rey de Diamantes
16.8 Glosario                                                         181

Mano   Chris contiene
Sota   de Diamantes
 Rey   de Tr´boles
            e
  10   de Corazones

Mano Allen: eligi´ Rey de Diamantes
                  o
Mano Allen: Rey de Corazones con Rey de Diamantes
Mano Jeff: eligi´ 10 de Corazones
                o
Mano Chris: eligi´ Sota de Tr´boles
                  o          e
Mano Allen: eligi´ Sota de Corazones
                  o
Mano Jeff: eligi´ Sota de Diamantes
                o
Mano Chris: eligi´ Reina de Picas
                  o
Mano Allen: eligi´ Sota de Diamantes
                  o
Mano Allen: Sota de Corazones con Sota de Diamantes
Mano Jeff: eligi´ Rey de Tr´boles
                o          e
Mano Chris: eligi´ Rey de Picas
                  o
Mano Allen: eligi´ 10 de Corazones
                  o
Mano Allen: 10 de Diamantes con 10 de Corazones
Mano Jeff: eligi´ Reina de Picas
                o
Mano Chris: eligi´ Sota de Picas
                  o
Mano Chris: Sota de Tr´boles con Sota de Picas
                      e
Mano Jeff: eligi´ Rey de Picas
                o
Mano Jeff: Rey de Tr´boles con Rey de Picas
                    e
----- El juego termin´.
                      o


La mano de Allen est´ vac´a.
                    a    ı

La mano de Jeff contiene
Reina de Picas

La mano de Chris est´ vac´a.
                    a    ı


As´ que Jeff es quien perdi´.
  ı                       o



16.8.      Glosario
herencia: 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                                                               Herencia

clase hija: Una nueva clase creada heredando de una clase existente; tambi´n
                                                                          e
     se la llama “subclase”.
Cap´
   ıtulo 17

Listas enlazadas

17.1.      Referencias incrustadas
Hemos visto ejemplos de atributos que hacen referencia a otros objetos, a los
que llamamos referencias incrustadas (v´ase la Secci´n 12.8). Una estrutura
                                         e            o
de datos com´n, la lista enlazada, saca partido de esta caracter´
            u                                                   ıstica.
Las listas enlazadas se componen de nodos, donde cada nodo contiene una
referencia al pr´ximo nodo de la lista. Adem´s, cada nodo contiene una unidad
                o                           a
de datos llamada carga
Podemos 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.
                                                  e


17.2.      La clase Nodo
Como es habitual cuando se escribe una clase, comenzaremos con los m´todos
                                                                    e
de inicializaci´n y str , para poder comprobar el mecanismo b´sico de crear
               o                                             a
y mostrar el nuevo tipo:
184                                                            Listas enlazadas

class 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                  o
les. 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
                                         o
en una lista.
Para comprobar la implementaci´n en este punto, podemos crear un Nodo e
                              o
imprimirlo:
>>> nodo = Nodo("prueba")
>>> print nodo
prueba
Para hacerlo m´s interesante, necesitaremos una lista que contenga m´s de un
              a                                                     a
nodo:
>>> 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                            u
todav´ 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        None

Para enlazar los nodos, debemos hacer que el primer nodo haga referencia al
segundo, y que ´ste haga referencia al tercero:
               e
>>> nodo1.siguiente = nodo2
>>> nodo2.siguiente = nodo3
La referencia del tercer nodo ser´ None, que indica que es el final de la lista.
                                 a
Ahora 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       None


Ahora ya sabe c´mo crear nodos y enlazarlos en listas. Lo que podr´ estar
                o                                                 ıa
menos claro es por qu´.
                     e




17.3.       Listas como colecciones
Las listas son utiles porque aportan un modo de ensamblar m´ltiples objetos
                                                                 u
dentro de una unica entidad, a veces llamada colecci´n. En el ejemplo, el primer
               ´                                     o
nodo 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        o
nodo. Por ejemplo, la funci´n imprimeLista toma como argumento un nodo
                            o
simple. Empezando con la cabeza de la lista, imprime cada nodo hasta que llega
al final.

def imprimeLista(nodo):
  while nodo:
    print nodo,
    nodo = nodo.siguiente
  print

Para llamar a este m´todo, pasamos una referencia al primer nodo:
                    e

>>> imprimeLista(nodo1)
1 2 3

Dentro de imprimeLista tenemos una referencia al primer nodo de la lista, pero
no hay una variable que haga referencia a los otros nodos. Tendremos que usar
el valor de siguiente de cada nodo para acceder al siguiente nodo.

Para recorrer una lista enlazada es habitual usar la variable de un bucle como
nodo 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 recursividad
Es natural expresar muchas operaciones con listas por medio de m´todos re-
                                                                     e
cursivos. El siguiente ejemplo es un algoritmo recursivo para imprimir una lista
hacia 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 manera
de imprimir una lista del rev´s. Pero si suponemos que la llamada recursiva
                               e
funciona —el acto de fe— entonces podemos estar convencidos de que este
algoritmo funciona.

Todo lo que necesitamos es un caso b´sico y una forma de demostrar que para
                                       a
cualquier lista podemos obtener el caso b´sico. Dada la definici´n recursiva de
                                            a                   o
una 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                                                         187

La primera l´ınea maneja el caso inicial no haciendo nada. Las dos siguientes
l´
 ıneas dividen la lista en cabeza y cola. Las dos ultimas l´
                                                      ´       ıneas imprimen la
lista. La coma que hay al final de la ultima l´
                                     ´       ınea evita que Python salte de l´
                                                                             ınea
despu´s de imprimir un nodo.
       e

Llamamos este m´todo tal como antes invocamos a imprimeLista:
               e

>>> imprimeAlReves(nodo1)
3 2 1

El resultado es una lista invertida.

Es posible que se pregunte por qu´ imprimeLista e imprimeAlReves son fun-
                                    e
ciones y no m´todos de la clase Nodo. La raz´n es que queremos usar None para
              e                              o
representar la lista vac´ y no es legal llamar un m´todo sobre None. Esta limi-
                        ıa                         e
taci´n resulta algo inc´moda a la hora de escribir c´digo para manipular listas
    o                   o                           o
con 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
                                a
listas har´n que el m´todo no funcione.
          a          e



17.5.      Listas infinitas
No hay nada que impida a un nodo hacer referencia a un nodo anterior de
la lista, incluido ´l mismo. Por ejemplo, esta figura muestra una lista con dos
                   e
nodos, uno de los cuales apunta a s´ mismo:
                                    ı
                   list



                     cargo        1         cargo         2
                          next                next



Si llamamos a imprimeLista sobre esta lista, entrar´ en un bucle infinito. Si lla-
                                                    a
mamos a imprimeAlReves, lo har´ de forma infinitamente recursiva. Eeste tipo
                               a
de comportamiento da lugar a que sea muy dif´ trabajar con listas infinitas.
                                               ıcil

Sin embargo, en ocasiones resultan utiles. Por ejemplo, podr´
                                    ´                         ıamos representar
un n´mero como una lista de d´
     u                         ıgitos y usar una lista infinita para representar
una fracci´n repetida.
          o
188                                                        Listas enlazadas

A pesar de todo, es un problema que no podamos probar que imprimeLista e
imprimeAlReves puedan terminar correctamente. Lo mejor que podemos hacer
es afirmar que “si la lista no contiene bucles, estos m´todos podr´n terminar”.
                                                      e          a
Este tipo de afirmaciones se conocen como condiciones previas. Imponen
una restricci´n sobre uno de los par´metros y describen el comportamiento del
             o                        a
m´todo si la restricci´n se satisface. Veremos m´s ejemplos m´s adelante.
  e                   o                          a            a



17.6.      Teorema fundamental de la ambig¨ edad
                                          u
Una parte de imprimeAlReves podr´ habernos sorprendido:
                                ıa

      cabeza = lista
      cola = lista.siguiente

Despu´s de la primera asignaci´n, la cabeza y la cola tienen el mismo tipo y
      e                        o
el mismo valor. Asi que, ¿para qu´ hemos creado un nueva variable?
                                 e

La raz´n es que las dos variables desempe˜an papeles diferentes. Pensamos en
      o                                  n
la cabeza como una referencia al primer nodo de la lista. Estos “papeles” no
forman parte del programa, sino que est´n en la mente del programador.
                                       a

En general no podemos decir con s´lo mirar un programa qu´ papel desem-
                                     o                      e
pe˜ar´ un variable. Esta ambig¨edad puede ser util, pero tambi´n puede difi-
  n a                           u               ´             e
cultar la lectura del programa. A menudo usaremos nombres para las variables
como nodo y lista para explicar c´mo queremos usar una variable y a veces
                                    o
creamos variables adicionales para eliminar ambig¨edades.
                                                 u

Podr´ıamos haber escrito imprimeAlReves sin cabeza ni cola, que lo har´ mas
                                                                      ıa
conciso, 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 sus
argumentos como una colecci´n y print los trata como a un s´lo objeto.
                           o                               o

El teorema fundamental de la ambig¨ edad indica que la ambig¨edad que
                                         u                  u
es 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                                                       189

17.7.         Modificar listas
Hay dos formas de modificar una lista enlazada. Obviamente, podemos cambiar
la carga de uno de los nodos, pero las operaciones m´s interesantes son las que
                                                    a
a˜aden, quitan o reordenan los nodos.
 n
Como ejemplo, escribamos un m´todo que quite el segundo nodo de la lista y
                                e
devuelva 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 segundo
De nuevo, estamos usando variables temporales para hacer m´s legible el c´digo.
                                                          a              o
As´ es como usamos este m´todo:
  ı                       e
>>>   imprimeLista(nodo1)
1 2   3
>>>   eliminado = elimnaSegundo(nodo1)
>>>   imprimeLista(eliminado)
2
>>>   imprimeLista(nodo1)
1 3
El 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                    ıa
argumento? ¿Hay una condici´n previa para este m´todo? Si es as´ ser´ algo
                            o                    e              ı,    ıa
razonable establecer un m´todo para manejar una violaci´n de la condici´n
                         e                              o                 o
previa.
190                                                           Listas enlazadas

17.8.      Envoltorios y ayudantes
A menudo es util dividir una operaci´n de listas en dos m´todos. Por ejemplo,
             ´                       o                   e
para imprimir una lista invertida en el formato convencional [3, 2, 1] pode-
mos usar el m´todo imprimeAlReves para imprimir 3, 2, pero necesitaremos
             e
un m´todo aparte para imprimir los corchetes y el primer nodo. Llam´moslo
     e                                                                 e
imprimeAlRevesBonito:
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            e
funcionan con casos especiales como una lista vac´ o un singleton.
                                                 ıa
Cuando usamos este m´todo en alg´n otro lugar del programa, llamamos direc-
                      e         u
tamente a imprimeAlRevesBonito, y ´ste llama a imprimeAlReves en nuestro
                                   e
lugar. En cierto modo, imprimeAlRevesBonito act´a como un envoltorio, y
                                                 u
utiliza a imprimeAlReves como su ayudante.


17.9.      La clase ListaEnlazada
Existen ciertos problemas delicados con la forma en que se implementaron las
listas. Inviertiendo el orden de causa y efecto, propondremos primero una im-
plementaci´n alternativa y explicaremos luego los problemas que ´sta resuelve.
            o                                                    e
En 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 primer
nodo. Los objetos ListaEnlazada sirven de asas para la manipulaci´n de las
                                                                        o
listas de los objetos de tipo Nodo:
class ListaEnlazada :
  def __init__(self) :
    self.longitud = 0
    self.cabeza   = None
Una ventaja de la clase ListaEnlazada es que suministra un marco natural
para poner funciones-envoltorio como imprimeAlRevesBonito, que podemos
convertir en un m´todo de la clase ListaEnlazada:
                 e
17.10 Invariantes                                                               191

class 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.
                            a
Ahora hay dos m´todos llamados imprimeAlReves: uno en la clase Nodo (el
                  e
ayudante), y otro en la clase ListaEnlazada (el envoltorio). Cuando el envolto-
rio llama a self.cabeza.imprimeAlReves, est´ llamando al ayudante, ya que
                                               a
self.cabeza es un objeto de tipo Nodo.
Otra ventaja de la clase ListaEnlazada es que facilita la forma de a˜adir o qui-
                                                                    n
tar el primer elemento de una lista. Por ejemplo, agregaPrimero es un m´todo
                                                                          e
para ListaEnlazada; toma un elemento de la carga como argumento y lo coloca
en 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 + 1
Como suele ser usual, deber´ ıamos comprobar ´ste c´digo para ver si maneja
                                                e      o
casos especiales. Por ejemplo, ¿qu´ ocurrir´ si la lista est´ unicialmente vac´
                                  e        ıa               a                 ıa?


17.10.        Invariantes
Algunas listas est´n “bien construidas”; otras no. Por ejemplo, si una lista con-
                    a
tiene un bucle, provocar´ que nuestros m´todos se cuelguen, as´ que podr´
                           a               e                       ı           ıamos
exigir que las listas no contengan bucles. Otro requisito es que el valor de el valor
192                                                           Listas enlazadas

de longitud en el objeto de tipo ListaEnlazada deber´ ser igual al n´mero
                                                    ıa              u
verdadero 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
   ıan
para objetos es una pr´ctica util de la programaci´n porque hace m´s f´cil de-
                       a      ´                   o                 a a
mostrar la idoneidad del c´digo, comprobar la integridad de las estructuras de
                           o
datos y la detecci´n de errores.
                  o

Una cosa que a veces confunde respecto a los invariantes es que en ocasiones son
violados. Por ejemplo, en medio de agregaPrimero, despu´s de a˜adir el nodo
                                                            e       n
paro antes de incrementar la longitud, se viola el invariante. Se acepta este tipo
de violaci´n; de hecho, a menudo es imposible modificar un objeto sin violar un
          o
invariante durante al menos un corto espacio de tiempo. Normalmente, se exige
que todo m´todo que viole un invariante debe restablecerlo.
            e

Si hay alg´n tramo significativo de c´digo en el que el invariante se ve violado,
          u                         o
es importante que los comentarios lo dejen claro, de modo que no se realicen
operaciones que dependan del invariante.



17.11.       Glosario
referencia 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                                                        193

envoltorio: 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                                     o

invariante: Afirmaci´n que deber´ ser cierta para un objeto en todo momento
                     o            ıa
     (excepto tal vez cuando se est´ modificando el objeto).
                                   a
Todo Sobre El Lenguaje Python
Cap´
   ıtulo 18

Pilas

18.1.      Tipos abstractos de datos
Los tipos de datos vistos hasta ahora han sido todos concretos, en el sentido
que se ha especificado su implementaci´n completamente. Por ejemplo, la clase
                                     o
Carta representa un naipe por medio de dos enteros. Como dijimos, esa no es
la 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              a
la 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                                                                                Pilas

Cuando hablamos de TADs a menudo se hace la distinci´n entre el c´digo que
                                                      o          o
usa el TAD, el c´digo cliente, y el c´digo que implementa el TAD, el c´digo
                o                    o                                o
proveedor.



18.2.        El TAD Pila
En este cap´ıtulo se presentar´ un TAD com´n, la pila. Una pila es una colecci´n,
                              a            u                                  o
lo que significa que es una estructura de datos que contiene elementos m´ltiples.
                                                                         u
Otras 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
    e
operaciones1 :

  init : Inicializar una pila nueva y vac´
                                         ıa.
push: A˜adir un elemento a la pila.
       n
pop: Extraer un elemento de la pila. El elemento devuelto siempre es el ultimo
                                                                        ´
     que se a˜adi´.
             n o
isEmpty: 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
             hon
Las operaciones sobre listas que posee Python son parecidas a las operaciones
que definen a una pila. La interfaz no es exactamente la que deber´ de ser, pero
                                                                 ıa
se pueden desarrollar programas para convertir el TAD Pila a las operaciones
predefinidas.
A este programa se lo llama implementaci´n del TAD Pila. En general, una
                                           o
implementaci´n es un conjunto de m´todos que satisfacen los prerrequisitos
              o                          e
sint´cticos y sem´nticos de la interfaz.
    a            a
He 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
                                                     a
lenguajes.
18.4 Uso de push y pop                                                      197

class 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                    ıa
Para meter un elemento nuevo en la pila, push lo apila en elementos. Para
quitar un elemento de la pila, pop utiliza el m´todo de lista hom´nimo2 para
                                                 e               o
quitar 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                              e
m´todos existentes, se llama enchapado. En la vida real, un enchapado es una
  e
capa fina de madera de alta calidad que se utiliza en la fabricaci´n de muebles
                                                                 o
para ocultar madera de baja calidad. Los cient´ıficos inform´ticos utilizan esta
                                                            a
met´fora para describir una parte de un programa que esconde los detalles de
    a
una implementaci´n y que provee una interfaz mas simple o mas est´ndar.
                 o                                                  a


18.4.         Uso de push y pop
Una pila es una estructura gen´rica de datos, lo significa que se puede a˜adir
                                 e                                         n
cualquier tipo de elementos a ella. El ejemplo mete en la pila dos enteros y una
cadena:
>>>   s = Stack()
>>>   s.push(54)
>>>   s.push(45)
>>>   s.push("+")
Se pueden utilizar isEmpty y pop para quitar e imprimir todos los elementos en
la pila:
  2 del   mismo nombre
198                                                                        Pilas

while not s.isEmpty() :
  print s.pop(),
La salida es + 45 54. En otras palabras, ¡se ha utilizado una pila para imprimir
los elementos en orden inverso! Reconocemos que no es el formato est´ndar de
                                                                        a
impresi´n de listas, pero fue muy f´cil usar una pila para lograrlo.
        o                          a
Compare estas l´  ıneas con la implementaci´n de imprimeAlReves que vimos
                                             o
en la Secci´n 17.4. Existe un paralelo natural entre la versi´n recurrente de
            o                                                   o
imprimeAlReves y el algoritmo de pila que acabamos de ver. La diferencia es
que imprimeAlReves utiliza la pila de tiempo de ejecuci´n para mantenerse al
                                                           o
tanto de los nodos mientras recorre la lista y luego los imprime cuando regresa
de la recursi´n. El algoritmo de pila hace lo mismo, pero utiliza un objeto Pila
             o
en vez de la pila de tiempo de ejecuci´n.
                                       o


18.5.      Usar una pila para evaluar postfijo
Las expresiones matem´ticas en la mayor´ de lenguajes de programaci´n se
                        a                 ıa                              o
escriben con el operador entre los dos operandos, de esta manera: 1+2. A este
formato se le llama infijo. Algunas calculadoras utilizan un formato alternativo
llamado 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 *.
                                                         o

Este ejemplo demuestra una de las ventajas de el formato postfijo—no hay
necesidad de usar par´ntesis para controlar el orden de operaciones. Para obtener
                     e
el 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                                                                   199

18.6.        An´lisis sint´ctico
               a          a
Para implementar el algoritmo anterior, necesitamos ser capaces de recorrer una
cadena y separarla en operandos y operadores. Este proceso es un ejemplo del
an´lisis sint´ctico, y los resultados—la piezas individuales de la cadena—son
  a          a
tokens3 . 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                                      o
y en el m´dulo re (expresiones regulares). La funci´n string.split parte una
         o                                            o
cadena y la convierte en una lista utilizando un s´lo car´cter como delimitador.
                                                  o      a
Por 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
                                      a
espacio.

La funci´n re.split tiene m´s potente, y nos permite utilizar una expresi´n
         o                     a                                                o
regular en vez de un delimitador. Una expresi´n regular es una manera de espe-
                                                o
cificar un conjunto de cadenas. Por ejemplo, [A-z] es el conjunto de todas las
letras 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
                                                     u
lo 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; el
delimitador 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                   e



18.7.        Evaluar un postfijo
Para evaluar una expresi´n en formato postfijo usaremos el analizador sint´ctico
                          o                                                  a
de 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         o
a˜adir´ confusi´n.
 n     ıa        o
200                                                                   Pilas

las 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 lo
dem´s es un operando. Por supuesto, ser´ mejor verificar si la entrada tiene
    a                                     ıa
errores y mostrar un mensaje con el error, pero eso se har´ despu´s.
                                                          a      e

Comprobemos con una evaluaci´n de la forma postfijo (56+47)*2):
                            o

>>> print evalPostfijo ("56 47 + 2 *")
206

Esto es suficiente.



18.8.      Clientes y proveedores
Uno de los objetivos fundamentales de un TAD es el de separar los intereses
del proveedor, quien escribe el c´digo de programa que implementa el TAD,
                                  o
y los del cliente, quien utiliza el TAD. El proveedor s´lo se preocupa de la
                                                       o
implementaci´n y de si es correcta o no—de acuerdo a las especificaciones del
              o
TAD—y no de c´mo se va a utilizar.
                 o
18.9 Glosario                                                              201

A la inversa, el cliente supone que la implementaci´n del TAD es correcta y
                                                    o
no se preocupa de los detalles. Cuando se usa uno de los tipos predefinidos de
Python, 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                    o
go de cliente para probarlo. En ese case, uno toma ambos papeles, lo cual puede
causar confusi´n. Tiene que fijarse bien en del papel que est´ tomando en todo
               o                                            a
momento.


18.9.      Glosario
tipo 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            a
cliente: Un programa (o la persona que lo escribi´) que utiliza un TAD.
                                                 o
proveedor: 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.
                                                             a
estructura 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.
          e
analizar sint´cticamente: Examinar una cadena de caracteres o tokens y
              a
     analizar su estructura gram´tical.
                                a
token: Un conjunto de caracteres que se tratan como una unidad y son anali-
     zados sint´cticamente, como las palabras de un lenguaje natural.
               a
delimitador: Un car´cter utilizado para separar tokens, como la puntuaci´n
                     a                                                  o
     en un lenguaje natural.
Todo Sobre El Lenguaje Python
Cap´
   ıtulo 19

Colas

Este 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
                                                                 u
mayor´ de los casos, el primer cliente de la fila es el primero al que se va a
       ıa
servir. Sin embargo, hay excepciones. En los aeropuertos, a veces se saca de la
cola a los clientes cuyos vuelos van a salir pronto. En los supermercados, un
cliente 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                          a
La 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                          a
general es el encolamiento priorizado, en la que a cada cliente se le asigna
una prioridad y el cliente con la prioridad m´s alta pasa primero, sin importar
                                                 a
el orden de llegada. Decimos que es la t´ctica m´s general porque la prioridad
                                            a        a
se puede basar en cualquier cosa: a qu´ hora sale el vuelo; cu´ntos productos
                                           e                       a
lleva el cliente; cu´n importante es el cliente. Por supuesto, no todas las t´cticas
                    a                                                        a
de 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                                           a
FIFO, y una cola priorizada (como su propio nombre indica) usa una t´ctica de
                                                                     a
encolamiento priorizado.



19.1.       El TAD Cola
El TAD Cola se define a trav´s de las siguientes operaciones:
                           e

 init : Inicializa una cola nueva vac´
                                     ıa.
204                                                                   Colas

inserta: A˜ade un elemento a la cola.
          n

quita: Elimina y devuelve un elemento de la cola. El elemento devuelto es el
     primero que se a˜adi´.
                     n o

estaVacia: Comprueba si la cola est´ vac´
                                   a    ıa.



19.2.     Cola Enlazada
La primera implementaci´n del TAD Cola al que vamos a echar un vistazo se
                       o
llama cola enlazada porque est´ hecha de ojetos Nodo enlazados. He aqu´ la
                               a                                      ı
definici´n de la clase:
       o

class 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 carga

Los m´todos estaVacia y quita son id´nticos a los m´todos estaVacia y
     e                               e              e
quitaPrimero de ListaEnlazada. El m´todo inserta es nuevo y un poco m´s
                                   e                                 a
19.3 Rendimiento t´
                  ıpico                                                     205

complicado.

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 de
longitud deber´ ser el n´mero de nodos en la cola, y el ultimo nodo deber´
              ıa         u                              ´                ıa
tener siguiente igual a None. Cr´ase que este m´todo cumple con ambas inva-
                                e              e
riantes.




19.3.      Rendimiento t´
                        ıpico
Normalmente cuando invocamos un m´todo, no nos importan los detalles de su
                                    e
implementaci´n. Pero hay un “detalle” que podr´ interesarnos: el rendimien-
              o                                 ıa
to t´
    ıpico del m´todo. ¿Cu´nto tarda, y c´mo var´ el tiempo de ejecuci´n al
                 e       a              o        ıa                   o
aumentar el n´mero de elementos de la colecci´n?
               u                             o

Primero 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                               e
as´ se llama operaci´n de tiempo constante. En realidad, el m´todo podr´
  ı                 o                                            e           ıa
ser ligeramente m´s r´pido cuando la lista est´ vac´ porque se salta el cuerpo
                  a a                           a    ıa
de la condici´n, pero esa diferencia no es significativa.
              o

El rendimiento de inserta es muy diferente. En el caso general, tenemos que
recorrer la lista para encontrar el ultimo elemento.
                                    ´

Este recorrido cuesta un tiempo proporcional a la longitud de la lista. Como el
tiempo de ejecuci´n es funci´n lineal de la longitud, este m´todo se llama de
                  o          o                              e
tiempo lineal. Comparado con el tiempo constante, es muy pobre.




19.4.      Cola Enlazada Mejorada
Nos gustar´ una implementaci´n del TAD Cola capaz de realizar todas las
           ıa                 o
operaciones en tiempo constante. Una forma de hacerlo es modificar la clase
Cola 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                 next

La 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
                ´                                                  e
inserta 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 + 1

Como ultimo sigue el rastro del ultimo nodo, no necesitamos buscarlo. A causa
                                ´
de esto, este m´todo funciona en tiempo constante.
               e

Debemos pagar un precio por esa velocidad. Tenemos que a˜adir un caso especial
                                                        n
a 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 carga
Esta implementaci´n es m´s complicada que la de la Lista Enlazada, y es m´s
                 o      a                                                a
dif´ demostrar que es correcta. La ventaja es que hemos alcanzado la meta:
   ıcil
tanto 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 priorizada
El TAD Cola Priorizada tiene el mismo interfaz que el TAD Cola, pero diferente
sem´ntica. De nuevo, el interfaz es:
    a

 init : Inicializa una cola vac´ nueva.
                               ıa
inserta: A˜ade un nuevo elemento a la cola.
          n
quita: Elimina y devuelve un elemento de la cola. El elemento devuelto es el
     de prioridad m´s alta.
                   a
estaVacia: Comprueba si la cola est´ vac´
                                   a    ıa.

La diferencia sem´ntica es que el elemento eliminado de la cola no es necesa-
                  a
riamente el primero que se a˜adi´. En su lugar, es el elemento con la prioridad
                            n o
m´s alta. Lo que son las prioridades y c´mo se comparan con las otras no se
  a                                      o
especifica en la implementaci´n de la Cola Priorizada. Depende de los elementos
                            o
de la cola.
Por ejemplo, si los elementos de la cola tienen nombres, podemos elegirlos en
orden alfab´tico. Si son puntuaciones de bolos, podemos ir de mayor a menor,
            e
pero si son puntuaciones de golf, iremos de menor a mayor. Siempre que podamos
comparar los elementos de la cola, podremos encontrar y quitar el elemento con
mayor prioridad.
Esta implementaci´n de Cola Priorizada tiene como atributo una lista de Python
                   o
que contiene los elementos de la cola.
208                                                                     Colas

class 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                    o
operaciones sobre listas. El unico m´todo interesante es quita:
                             ´      e

class 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 elemento

Al 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-
             a
pleta el bucle, el programa compara el i´simo elemento con el campe´n. Si el
                                          e                             o
nuevo 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 elemento
mayor. 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
                                                       a
14
13
12
11
19.6 La clase Golfista                                                    209

Si la cola contiene n´meros o cadenas simples, se eliminan en orden num´rico
                     u                                                  e
o alfab´tico, de mayor a menor. Python puede encontrar el entero o la cadena
       e
mayor porque puede compararlos usando los operadores de comparaci´n inter-
                                                                    o
nos.
Si la cola contiene un tipo de objeto, debe proporcionar un m´todo cmp .
                                                               e
Cuando quita usa el operador > para comparar elementos, invoca al cmp de
uno 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´. a



19.6.      La clase Golfista
Como ejemplo de un objeto con una definici´n inusual de prioridad, vamos a
                                            o
implementar 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 puntuaciones
en bonitas columnas.
A continuaci´n definimos una versi´n de cmp en la que la puntuaci´n m´s
             o                     o                                  o a
baja 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 0

Ya 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    : 61
J.M. Olaz´bal : 69
         a
´ngel Cabrera : 72
A

      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.
                  o



19.7.      Glosario
cola: Un conjunto ordenado de objetos esperando un servicio de alg´n tipo.
                                                                  u

Cola: 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      o

FIFO: “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.
         n

tiempo 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

´
Arboles

Al igual que las listas enlazadas, los ´rboles est´n hechos de nodos. Un tipo
                                       a          a
com´n de ´rbol es un ´rbol binario, en el que cada nodo contiene una referencia
    u     a           a
a otros dos nodos (posiblemente nula). Nombramos a estas referencias como
sub´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           None

Para 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         a
extremos con referencias nulas se llaman hojas. Puede parecer extra˜o quen
dibujemos la figura con la ra´ arriba y las hojas abajo, pero eso no es lo m´s
                               ız                                            a
raro.

Para empeorar las cosas, los cient´ıficos inform´ticos a˜aden a la mezcla otra
                                               a       n
met´fofa: el ´rbol geneal´gico. El nodo superior se llama a veces padre y los
   a         a           o
212                                                                    ´
                                                                       Arboles

nodos a los que se refiere son sus hijos. Los nodos con el mismo padre se llaman
hermanos.
Para terminar, tenemos un vocabulario geom´trico para hablar sobre los ´rboles.
                                           e                           a
Ya hemos mencionado izquierda y derecha, pero tambi´n est´n “arriba” (hacia
                                                     e     a
el padre/ra´ y “abajo” (hacia los hijos/hojas). Tambi´n, todos los nodos que
           ız)                                        e
est´n a la misma distancia de la ra´ forman un nivel del ´rbol.
   a                               ız                    a
Probablemente no sean necesarias las met´foras arb´reas para hablar de ´rboles,
                                        a         o                    a
pero ah´ est´n.
       ı    a
Igual que las listas enlazadas, los ´rboles son estructuras de datos recursivas
                                    a
porque 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.
                  a



20.1.      Crear ´rboles
                 a
El proceso de montar un ´rbol es similar al proceso de montar una lista enlazada.
                        a
Cada invocaci´n del constructor crea un solo nodo.
              o
class 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
                                                  a
deben ser ´rboles. Tanto izquierda como derecha son opcionales; el valor por
          a
omisi´n es None.
     o
Para imprimir un nodo, simplemente imprimimos la carga.
Una forma de construir un ´rbol es del fondo hacia arriba. Asigne primero los
                          a
nodos hijos:
izquierda = Arbol(2)
derecha = Arbol(3)
20.2 Recorrer ´rboles
              a                                                           213

Luego cree el nodo padre y vinc´lelo a los hijos:
                               u

arbol = Arbol(1, izquierda, derecha);

Podemos escribir este c´digo m´s concisamente anidando las invocaciones al
                       o      a
constructor:

>>> 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
                    a

Siempre que usted vea una nueva estructura de datos, su primera pregunta
deber´ ser: “¿C´mo la recorro?” La forma m´s natural de recorrer un ´rbol
      ıa        o                             a                         a
es recursivamente. Por ejemplo, si el ´rbol contiene enteros como carga, esta
                                      a
funci´n nos devuelve su suma:
     o

def total(arbol):
  if arbol == None: return 0
  return total(arbol.izquierda) + total(arbol.derecha) +
    arbol.carga

El 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
                                                                       a
hijos. Cuando terminan las llamadas recursivas, sumamos la carga del padre y
devolvemos el total.




20.3.      ´
           Arboles de expresi´n
                             o

Un ´rbol es un forma natural de representar la estructura de una expresi´n.
    a                                                                   o
Al contrario que otras notaciones, puede representar el c´lculo de forma no
                                                            a
ambigua. Por ejemplo, la expresi´n infija 1 + 2 * 3 es ambigua a no ser que
                                 o
sepamos 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    right



Los nodos de un ´rbol de expresi´n pueden ser operandos como 1 y 2 u operado-
                 a              o
res como + y *. Los operandos son nodos hojas; los nodos operadores contienen
referencias 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
                                                                           o
se 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 se
a
usan dentro de los compiladores para analizar, optimizar y traducir programas.



20.4.      Recorrido de un ´rbol
                           a
Podemos 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
                                      a
ra´ luego el sub´rbol izquierdo entero y despu´s el sub´rbol derecho entero.
  ız,            a                               e        a
Esta forma de recorrer un ´rbol se llama orden prefijo, porque el contenido de
                          a
la ra´ aparece antes del contenido de los hijos. La salida del ejemplo anterior
      ız
es:
20.4 Recorrido de un ´rbol
                     a                                                      215

>>> arbol = Arbol(’+’, Arbol(1), Arbol(’*’, Arbol(2), Arbol(3)))
>>> imprimeArbol(arbol)
+ 1 * 2 3
Este formato es diferente tanto del postfijo como del infijo; es otra notaci´n
                                                                          o
llamada 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                                  a
si´n en una notaci´n diferente. Por ejemplo, si imprime primero los sub´rboles
  o               o                                                      a
y luego la ra´ tendr´:
             ız,    a
def 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          o
llama orden postfijo.
Finalmente, para recorrer un ´rbol en orden infijo, usted imprime el ´rbol
                               a                                    a
izquierdo, luego la ra´ y despu´s el ´rbol derecho:
                      ız       e     a
def 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           o
Para ser justos debemos se˜alar que hemos omitido una importante compli-
                             n
caci´n. A veces, al escribir una expresi´n infija, debemos usar par´ntesis para
    o                                   o                         e
preservar el orden de las operacioens. De modo que una exploraci´n de orden
                                                                  o
infijo no es suficiente para generar una expresi´n infija.
                                               o
No obstante, con unas peque˜as mejoras, el ´rbol de expresi´n y los tres re-
                           n               a               o
corridos nos dan una forma general de traducr expresiones de un formato a
otro.

     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                                     e

Si 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                                                                    ´
                                                                       Arboles

def 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              o
incialmente es 0. Cada vez que hacemos una llamada recursiva, pasamos nivel+1
porque el nivel del hijo es siempre uno mayor que el del padre. Sangramos cada
elemento con dos espacios por nivel. El resultado del ´rbol de ejemplo es:
                                                      a

>>> imprimeArbolSangrado(arbol)
    3
  *
    2
+
  1

Si mira la salida de lado, ver´ una versi´n simplificada de la figura original.
                              a          o



20.5.      Construir un ´rbol de expresi´n
                        a               o
En 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 siguiente
a                 o                           o
´rbol:
a


                                         *
                                    +          9


                               3          7

F´
 ıjese en que hemos simplificando el diagrama omitiendo los nombres de los
atributos.

El analizador que vamos a escribir maneja expresiones que incluyen n´meros,
                                                                    u
par´ntesis y los operadores + y *. Suponemos que la cadena de entrada ya ha
   e
sido 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     a
lista.

      A modo de ejercicio, escriba una funci´n que tome una cadena con-
                                            o
      teniendo una expresi´n y devuelva una lista de tokens.
                          o

La primera funci´n que vamos a escribir es tomaToken, que toma como par´me-
                 o                                                               a
tros una lista de tokens y el token que esperamos. Compara el token esperado
con el primer token de la lista: si coinciden, elimina el token de la lista y devuelve
verdadero, en caso contrario, devuelve falso:
def tomaToken(listaToken, esperado):
  if listaToken[0] == esperado:
    del listaToken[0]
    return 1
  else:
    return 0
Como 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
                  o
de listaToken es un n´mero, obtieneNumero lo elimina y devuelve un nodo
                        u
hoja que contiene el n´mero; en caso contrario, devuelve None.
                      u
def 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 una
lista de n´meros a listaToken, extraemos el primero, imprimimos el resultado
          u
e 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 como
a               o                                                u
operandos, como 3 * 7.
218                                                                    ´
                                                                       Arboles

Aqu´ tenemos una versi´n de obtieneProducto que maneja productos simples.
   ı                  o
def obtieneProducto(listaToken):
  a = obtieneNumero(listaToken)
  if tomaToken(listaToken, ’*’):
    b = obtieneNumero(listaToken)
    return Arbol (’*’, a, b)
  else:
    return a
Suponiendo que obtieneNumero se ejecuta con ´xito y devuelve un ´rbol simple,
                                             e                   a
asignamos el primer operando a a. Si el siguiente car´cter es *, obtenemos el
                                                     a
segundo n´mero y construimos un ´rbol de expresi´n con a, b, y el operador.
         u                       a                o
Si el siguiente car´cter es cualquier otra cosa, simplemente devolvemos el nodo
                   a
hoja 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)
9
El segundo ejemplo implica que entendemos un operando suelto como un tipo de
producto. Esta definici´n de “producto” es contraria a la intuici´n, pero resulta
                      o                                         o
ser util.
    ´
Ahora tenemos que v´rnoslas con productos compuestos, como 3 * 5 * 13.
                       e
Tratamos esta expresi´n como un producto de productos, es decir, 3 * (5 *
                       o
13). El ´rbol resultante es:
        a


                                    *
                               3
                                         *
                                    5         13
Con un peque˜o cambio en obtieneProducto podemos manejar productos ar-
              n
bitrariamente largos:
20.5 Construir un ´rbol de expresi´n
                  a               o                                        219

def obtieneProducto(listaToken):
  a = obtieneNumero(listaToken)
  if tomaToken(listaToken, ’*’):
    b = obtieneProducto(listaToken)               # cambiamos esta l´nea
                                                                    ı
    return Arbol (’*’, a, b)
  else:
    return a

En otras palabras, un producto puede ser bien un operando aislado o un ´rbol
                                                                       a
con * en su ra´ un n´mero en la derecha y un producto en la izquierda. Este
              ız,     u
tipo de definici´n recursiva deber´ empezar a resultar familiar.
               o                 ıa
Comprobemos 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-
        n
ci´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,                                                             e
una suma puede ser simplemente un producto.
Si quiere seguir adelante con esta definici´n, tiene una bonita propiedad: pode-
                                          o
mos representar cualquier expresi´n (sin par´ntesis) como una suma de produc-
                                  o          e
tos. Esta propiedad es la base de nuestro algoritmo de an´lisis.
                                                          a
obtieneSuma intenta construir un ´rbol con un producto en la izquierda y una
                                  a
suma 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 a

Vamos a probarla con 9 * 11 + 5 * 7:

>>> listaToken = [9, ’*’, 11, ’+’, 5, ’*’, 7, ’fin’]
>>> arbol = obtieneSuma(listaToken)
>>> imprimeArbolPostfijo(arbol)
9 11 * 5 7 * +
220                                                                  ´
                                                                     Arboles

Ya casi hemos acabado, pero todav´ tenemos que manejar los par´ntesis.
                                   ıa                             e
En culaquier lugar de una expresi´n donde puede haber un n´mero, puede
                                 o                           u
haber tambi´n una suma entera entre par´ntesis. S´lo necesitamos modificar
           e                           e         o
obtieneNumero 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                      e
multiplicaci´n.
            o
En la versi´n final del programa, ser´ bueno dar a obtieneNumero un nombre
           o                        ıa
m´s descriptivo de su nueva funci´n.
 a                               o



20.6.      Manejar errores
En todo momento hemos supuesto que las expresiones estaban bien formadas.
Por ejemplo, cuando alcanzamos el final de una subexpresi´n, suponemos que
                                                            o
el car´cter siguiente es un par´ntesis cerrado. Si hay un error y el siguiente
      a                          e
car´cter es cualquier otra cosa, deber´
   a                                  ıamos ocuparnos de ´l.
                                                         e
def 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
                                  o
La sentencia raise crea una excepci´n; en este caso creamos un nuevo tipo de ex-
                                   o
cepci´n, llamado ExpresionErronea. Si la funci´n que llam´ a obtieneNumero,
     o                                           o          o
o alguna de las otras funciones de la pila de llamadas, maneja la expresi´n, el
                                                                          o
programa podr´ continuar. En caso contrario, Python imprimir´ un mensaje de
                a                                              a
error 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.
                o



20.7.      El ´rbol de animales
              a
En esta secci´n desarrollaremos un peque˜o programa que usa un ´rbol para
             o                          n                      a
representar una base de conocimientos.
El programa interact´a con el usuario para crear un ´rbol de preguntas y nom-
                    u                               a
bres de animales. Aqu´ tenemos un ejemplo de ejecuci´n:
                      ı                               o
Est´s pensando en un animal? s
    a
Es un p´jaro? n
       a
C´mo se llama el animal? perro
 o
Qu´ pregunta distinguir´a a un perro de un p´jaro? Puede volar
  e                    ı                    a
Si el animal fuera un perro, cu´l ser´a la respuesta? n
                               a     ı

Est´s pensando en un animal? s
    a
Puede volar? n
Es un perro? n
C´mo se llama el animal? gato
 o
Qu´ 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
   a
Puede volar? n
Ladra? s
Es un perro? s
Soy el m´s grande!
         a
222                                                                    ´
                                                                       Arboles

Est´s pensando en un animal? n
   a

Este es el ´rbol que construye este di´logo:
           a                          a

                                    Can it fly?

                              n                      y

                      Does it bark?                 bird

                      n            y
                     cat          dog

Al principio de cada ronda, el programa empieza en lo alto del ´rbol y hace la
                                                                   a
primera pregunta. Dependiendo de la respuesta, se mueve al hijo de la izquierda
o de la derecha y sigue hasta que llega a un nodo hoja. En ese momento, intenta
adivinar. Si falla, pide al usuario el nombre del nuevo animal y una pregunta que
distinga al intento fallido del nuevo animal. Entonces a˜ade un nodo al ´rbol
                                                           n                a
con la nueva pregunta y el nuevo animal.
´
Este es el c´digo:
            o

def 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
         o
usuario. Si la respuesta comienza con s o S, la funci´n devuelve verdadero:
                                                     o
def 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                                                    a
ejecute la sentencia break cuando el usuario ya no piense en ning´n animal.
                                                                  u
El bucle while interno recorre el ´rbol de arriba a abajo, guiado por las res-
                                  a
puestas del usuario.
Cuando se a˜ade un nuevo nodo al ´rbol, la pregunta sustituye a la carga y los
             n                      a
dos hijos son el animal nuevo y la carga original.
Una carencia del programa es que, al salir, ¡olvida todo lo que usted le hab´
                                                                            ıa
ense˜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                                                                 ´
                                                                    Arboles

20.8.      Glosario
´rbol binario: Un ´rbol en el que cada nodo apunta a cero, uno, o dos nodos
a                  a
     dependientes.

ra´ El nodo superior de un ´rbol, sin padre.
  ız:                      a

hoja: Un nodo del extremo inferior de un ´rbol, sin hijos.
                                         a

padre: 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.
                                       u

nivel: 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.
                                 o

orden 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
  e

Depuraci´n
        o

En 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                    e
Aunque las secciones que siguen est´n organizadas por tipos de error, algunas
                                    a
t´cnicas son aplicables en m´s de una situaci´n.
 e                          a                o


A.1.      Errores de sintaxis
Los errores de sintaxis suelen ser f´ciles de arreglar una vez que averigua
                                    a
lo que son. Desgraciadamente, muchas veces los mensajes de error no son
226                                                                 Depuraci´n
                                                                            o

muy utiles. Los mensajes m´s comunes son SyntaxError: invalid syntax y
    ´                     a
SyntaxError: 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                             o
En realidad, le dice d´nde not´ el problema Python, que no es necesariamente
                      o         o
donde est´ el error. A veces el error est´ antes de la localizaci´n del mensaje de
         a                               a                       o
error, muchas veces en la l´
                           ınea anterior.

Si est´ haciendo el programa incrementalmente, deber´ tener casi localizado el
      a                                             ıa
error. Estar´ en la ultima l´
            a       ´       ınea que a˜adi´.
                                      n o

Si est´ usted copiando c´digo de un libro, comience comparando con atenci´n
      a                   o                                                  o
su c´digo con el del libro. Compruebe cada car´cter. Al mismo tiempo, recuerde
    o                                         a
que el libro podr´ estar equivocado, as´ que si ve algo que parezca un error de
                 ıa                     ı
sintaxis, podr´ serlo.
              ıa

He 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                  ıa

Si nada funciona, siga con la secci´n que sigue...
                                   o
A.2 Errores en tiempo de ejecuci´n
                                o                                            227

A.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
                                                                   ıa
usted y el compilador no miran el mismo c´digo. Compruebe su entorno de
                                                o
programaci´n para asegurarse de que el programa que est´ editando es el que
            o                                              a
est´ intentando ejecutar Python. Si no est´ seguro, pruebe a poner un error de
   a                                         a
sintaxis obvio y deliberado al principio del programa. Ahora ejecute (o importe)
de nuevo. Si el compilador no encuentra el nuevo error probalemente hay algo
equivocado en el modo en que est´ configurado su entorno.
                                   a
Si esto ocurre, puede enfrentarse a ello empezando de nuevo con un programa
nuevo como “Hola, mundo”, y asegurarse de que puede hacer que funcione un
programa conocido. Luego a˜ada gradualmente los trozos del programa nuevo
                             n
al que funciona.


A.2.      Errores en tiempo de ejecuci´n
                                      o
Una vez que su programa es sint´cticamente correcto, Python pude importarlo
                               a
y al menos comenzar a ejecutarlo. ¿Qu´ podr´ ir mal?
                                     e     ıa


A.2.1.     Mi programa no hace nada de nada.
Este problema es muy com´n cuando su archivo consta de funciones y clases
                           u
pero en realidad no invoca nada para que empiece la ejecuci´n. Esto puede ser
                                                           o
intencionado cuando s´lo planea importar el m´dulo para suministrar clases y
                      o                      o
funciones.
Sin no es intencionado, aseg´rese de que est´ llamando a una funci´n que inicie
                             u                a                      o
la 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     a


A.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 una
recursi´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    a


Bucle Infinito
Si cree que tiene un bucle infinito y piensa que sabe qu´ bucle provoca el pro-
                                                          e
blema, a˜ada una sentencia print que imprima los valores de las variables de
         n
la condici´n al final del bucle junto con el valor de la condici´n.
          o                                                    o
Por 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)
                   o
Ahora, cuando ejecute el programa, ver´ tres l´
                                         a      ıneas de salida en cada vuelta del
bucle. En la ultima vuelta el valor de la condici´n deber´ ser false. Si el bucle
             ´                                   o        ıa
sigue ejecut´ndose, podr´ ver los valores de x e y, y podr´ averiguar por qu´ no
            a            a                                 a                  e
se actualizan correctamente.


Recursi´n Infinita
       o
Una recursi´n infinita casi siempre har´ que el programa se ejecute un rato y
           o                          a
luego 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                      o
comience por asegurarse de que hay un caso b´sico. En otras palabras, deber´
                                            a                              ıa
A.2 Errores en tiempo de ejecuci´n
                                o                                              229

haber una condici´n que haga que la funci´n devuelva un valor sin hacer otra
                  o                            o
llamada recursiva. Si no, necesita revisar el algoritmo y encontrar ese caso b´sico.
                                                                              a

Si hay un caso b´sico pero el programa no parece llegar hasta ´l, a˜ada una
                  a                                               e n
sentencia print que imprima los par´metros al principio de la funci´n o m´todo.
                                     a                             o     e
Cuando ahora ejecute el programa, ver´ unas pocas l´
                                          a              ıneas cada vez que se
invoque la funci´n o m´todo y all´ ver´ los par´metros. Si los par´metros no se
                o      e           ı   a       a                  a
acercan al caso b´sico, eso le dar´ alguna idea de por qu´ no lo hace.
                  a               a                       e


Flujo de Ejecuci´n
                o

Si no est´ seguro de qu´ curso sigue el flujo de ejecuci´n en su programa, a˜ada
         a             e                               o                   n
sentencias print al principio de cada funci´n con un mensaje como “entrando
                                             o
en la funci´n turur´”, donde turur´ es el nombre de la funci´n.
           o        u                u                          o

Cuando ahora ejecute el programa, imprimir´ una traza de cada funci´n a me-
                                          a                        o
dida que las vaya invocando.



A.2.3.     Cuando ejecuto el programa recibo una excepci´n.
                                                        o
Si algo va mal durante la ejecuci´n, Python imprime un mensaje que incluye el
                                 o
nombre de la excepci´n, la l´
                    o       ınea del programa donde sucedi´ el problema y una
                                                          o
traza inversa.

La traza inversa identifica la funci´n que se est´ ejecutando ahora y la funci´n
                                   o            a                             o
que 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 a
donde se encuentra. Tambi´n incluye los n´meros de las l´
                            e              u               ıneas de sus archivos
donde suceden todas esas llamadas.

El primer paso es examinar el lugar del programa donde sucede el error y ver
si puede adivinar lo que sucedi´. Estos son algunos de los errores en tiempo de
                               o
ejecuci´n m´s comunes:
       o    a

NameError: 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.
                                             o

TypeError: 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.
                    a

AttributeError: 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-
                                                              o
de terminar enterrado en informaci´n. Hay dos formas de atajar el problema:
                                     o
simplificar 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 un
formato que la haga m´s comprensible.
                       a
Para simplificar el programa puede hacer varias cosas. Primero, reducir la escala
del problema en el que est´ trabajando el programa. Por ejemplo, si est´ orde-
                          a                                              a
nando un vector, ordene un vector peque˜o. Si el programa acepta entradas del
                                        n
usuario, dele la entrada m´s simple que provoque el problema.
                          a
Segundo, limpie el programa. Elimine el c´digo muerto y reorganice el programa
                                           o
para 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
       a
a reescribir esa parte con una estructura m´s simple. Si sospecha de una funci´n
                                             a                                 o
grande, trate de trocearla en funciones menores y pru´belas separadamente.
                                                       e
A.3 Errores sem´nticos
               a                                                            231

El proceso de encontrar el caso m´ınimo de prueba le llevar´ a menudo al error.
                                                           a
Si se encuentra con que un programa funciona en una situaci´n pero no en otra,
                                                             o
eso le dar´ una pista sobre lo que ocurre.
          a

De forma parecida, la reescritura de una porci´n de c´digo puede ayudarle a
                                                 o      o
encontrar errores sutiles. Si hace un cambio que a usted le parece que no afecta
al programa, pero s´ lo hace, le dar´ una pista.
                    ı                a



A.3.      Errores sem´nticos
                     a
En cierto modo, los errores sem´nticos son los m´s dif´
                                a                a    ıciles de corregir, porque
el compilador y el sistema de ejecuci´n no proporcionan informaci´n sobre lo
                                      o                              o
que va mal. S´lo usted sabe lo que se supone que debe hacer el programa, y s´lo
             o                                                               o
usted sabe que no lo est´ haciendo.
                        a

El primer paso es hacer una concexi´n entre el texto del programa y el compor-
                                     o
tamiento que est´ usted viendo. Necesita una hip´tesis sobre lo que realmente
                   a                              o
est´ haciendo el programa. Una de las dificultades que nos encontramos para
   a
ello es la alta velocidad de los computadores.

A menudo desear´ ralentizar el progrma a una velocidad humana, y con al-
                  ıa
gunos programas depuradores podr´ hacerlo. Pero el tiempo que lleva colocar
                                  a
unas sentencias print en los lugares adecuadoes suele ser menor que el que
lleva configurar el depurador, poner y quitar puntos de interrupci´n y “hacer
                                                                 o
caminar” 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
                                                                              o

Para programar necesitar´ tener un modelo mental de c´mo funcionan los pro-
                         a                            o
gramas. Si escribe un programa que no hace lo que espera de ´l, muchas veces
                                                            e
el problema no estar´ en el programa, sino en su modelo mental.
                    a
La mejor manera de corregir su modelo mental es dividiendo el programa en sus
componentes (normalmente las funciones y m´todos) y probando cada compo-
                                              e
nente de forma independiente. Una vez que encuentre la discrepancia entre su
modelo y la realidad, podr´ solucionar el problema.
                          a
Por supuesto, deber´ ir haciendo y probando componentes tal como desarrolla
                   ıa
el programa. Si encuentra un problema, s´lo habr´ una peque˜a cantidad de
                                           o    a           n
c´digo nuevo del que no sabe si est´ correcto.
 o                                 a


A.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                      o
dif´
   ıciles de depurar. Suele ser una buena idea dividir una expesi´n compleja en
                                                                 o
una 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 a
tan documentaci´n adicional, y es m´s f´cil de depurar porque puede comprobar
                  o                     a a
los tipos de las variables intermedias y mostrar sus valores.
Otro problema que puede suceder con las expresiones grandes es que el orden de
evaluaci´n puede no ser el que usted esperaba. Por ejemplo, si est´ traduciendo
        o                                                         a
la expresi´n 2π a Python, podr´ escribir:
          o x                  ıa
y = x / 2 * math.pi;
Eso no es correcto, porque la multiplicaci´n y la divisi´n tienen la misma prece-
                                          o             o
dencia y se eval´an de izquierd a derecha. As´ que esa expresi´n calcula xπ/2.
                u                             ı                 o
Una 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             e
grama 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 a
las 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                     a
tunidad de imprimir el valor de retorno antes de volver. De nuevo, puede usar
una variable temporal. Por ejemplo, en lugar de:
return self.manos[i].eliminaCoincidencias()
podr´ excribir:
    ıa
cant = self.manos[i].eliminaCoincidencias()
return cant
Ahora 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         e
Cuando est´ calmado, piense en el programa. ¿Qu´ es lo que hace? ¿Cu´les
            e                                       e                      a
pueden 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                                e
A veces lleva tiempo encontrar un error. Muchas veces encontramos errores
cuando 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 de
quedarse dormido.
234                                                                 Depuraci´n
                                                                            o

A.3.5.     No, de verdad necesito ayuda.
Sucede. Incluso los mejores programadores se atascan de vez en cuando. A veces
trabaja durante tanto tiempo en un programa que no puede ver el error. Lo que
necesita es un par de ojos nuevos.
Antes de llamar a andie, aseg´rese de que ha agotado las t´cnicas explicadas
                              u                             e
aqu´ Su programa deber´ ser tan simple como sea posible, y usted deber´ estar
    ı.                  ıa                                              ıa
trabajando con la entrada m´
                           ınima que provoca el error. Deber´ tener sentencias
                                                            ıa
print en los lugares adecuados (y lo que dicen deber´ ser comprensible). De-
                                                     ıa
ber´ entender el problema lo bastante bien como para describirlo sucintamente.
   ıa
Cuando llame a alguien para que le ayude, aseg´rese de darles la informaci´n
                                              u                           o
que 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                              e

Cuando encuentre el error, t´mese un momento para pensar acerca de lo que
                             o
podr´ haber hecho para encontrarlo m´s r´pido. La siguiente vez que vea algo
    ıa                                  a a
parecido, ser´ capaz de encontrar el error antes.
             a
Recuerde, el objetivo no es s´lo hacer que el programa funciones. El objetivo es
                             o
aprender c´mo hacer funcionar al programa.
          o
Ap´ndice B
  e

Crear un nuevo tipo de
datos

Los lenguajes de programaci´n orientados a objetos permiten a los programado-
                             o
res crear nuevos tipos de datos que se comporten de manera muy parecida a los
tipos de datos nativos. Exploraremos esta posibilidad construyendo una clase
Fraccion que funcione de manera muy similar a los tipos num´ricos nativos,
                                                                e
enteros, enteros largos y flotantes.
Las fracciones, tambi´n conocidas como n´meros racionales, son valores que
                     e                    u
pueden expresrse como la proporci´n entre dos n´meros enteros, tal como 5/6.
                                  o            u
Al n´mero superior se se le llama numerador y al inferior se se le llama deno-
    u
minador.
Comenzamos definiendo la clase Fraccion con un m´todo de inicializaci´n que
                                                e                   o
nos surta de un numerador y un demonimador enteros:
class Fraccion:
  def __init__(self, numerador, denominador=1):
    self.numerador = numerador
    self.denominador = denominador
El denominador es opcional. Una Fraccion con un s´lo par´metro representa un
                                                 o       a
n´mero entero. Si el numerador es n, construimos la fracci´n n/1.
 u                                                        o
El siguente paso es escribir un m´todo str para que imprima las frac-
                                  e
ciones de forma que tenga sentido. La forma natural de hacerlo es “numera-
dor/denominador”:
236                                          Crear un nuevo tipo de datos

class Fraccion:
  ...
  def __str__(self):
    return "%d/%d" % (self.numerador, self.denominador)
Para probar lo que tenemos hasta ahora, lo ponemos en un fichero llamado
Fraccion.py y lo importamos desde el int´rprete de Python. Entonces creamos
                                        e
un objeto fracci´n y lo imprimimos.
                o
>>> from Fraccion import fraccion
>>> mortadela = Fraccion(5,6)
>>> print "La fracci´n es", mortadela
                    o
La fracci´n es 5/6
         o
Como siempre, la funci´n print invoca impl´
                      o                   ıcitamente al m´todo
                                                         e          str .



B.1.      Multiplicaci´n de fracciones
                      o
Nos gustar´ poder aplicar las operaciones normales de suma, resta, multiplica-
           ıa
ci´n y divisi´n a las fracciones. Para ello, podemos sobrecargar los operadores
  o          o
matem´ticos para los objetos de clase Fraccion.
       a
Comenzaremos con la multiplicaci´n porque es la m´s f´cil de implementar.
                                  o                  a a
Para multiplicar dos fraciones, creamos una nueva fracci´n cuyo numerador
                                                         o
es el producto de los numeradores de los operandos y cuyo denominador es
el producto de los denominadores de los operandos. mul es el nombre que
Python utiliza para el m´todo que sobrecarga al operador *:
                        e
class 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/24
Funciona, pero ¡podemos hacerlo mejor! Podemos ampliar el m´todo para ma-
                                                               e
nejar la multiplicaci´n por un entero. Usamos la funci´n type para ver si otro
                     o                                  o
es un entero y convertirlo en una fracci´n en tal caso.
                                        o
class 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                                  o
ci´n es el operando de la izquierda.
  o
>>> print Fraccion(5,6) * 4
20/6
>>> print 4 * Fraccion(5,6)
TypeError: __mul__ nor __rmul__ defined for these operands
Para evaluar un operador binario como la multiplicaci´n, Python comprueba
                                                       o
primero el operando de la izquierda para ver si proporciona un m´todo mul
                                                                e
que soporte el tipo del segundo operando. En este caso, el operador nativo de
multiplicaci´n del entero no soporta fracciones.
            o
Despu´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 provisto
el m´todo rmul , por lo que falla.
    e
Por 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               e
evaluamos 4 * Fraccion(5,6), Python llamar´ al m´todo rmul del objeto
                                          a      e
Fraccion y le pasar´ 4 como par´metro:
                   a            a
>>> print 4 * Fraccion(5,6)
20/6
Dado que rmul es lo mismo que        mul , y   mul   puede manejar un par´me-
                                                                         a
tro entero, ya est´ hecho.
                  a


B.2.      Suma de fracciones
La suma es m´s complicada que la multiplicaci´n, pero a´n es llevadera. La
             a                                o        u
suma de a/b y c/d es la fracci´n (a*d+c*b)/b*d.
                              o
Usando como modelo el c´digo de la multiplicaci´n, podemos escribir
                       o                       o                         add   y
 radd :
238                                          Crear un nuevo tipo de datos

class Fraccion:
  ...
  def __add__(self, otro):
    if type(otro) == type(5):
      otro = Fraccion(otro)
    return Fraccion(self.numerador   * otro.denominador +
                    self.denominador * otro.numerador,
                    self.denominador * otro.denominador)

  __radd__ = __add__

Podemos probar estos m´todos con Fracciones y enteros.
                      e

>>> print Fraccion(5,6) + Fraccion(5,6)
60/36
>>> print Fraccion(5,6) + 3
23/6
>>> print 2 + Fraccion(5,6)
17/6

Los dos primeros ejemplos llaman a    add ; el ultimo llama a
                                               ´                 radd .



B.3.      Algoritmo de Euclides
En 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    a
denominador por el m´ximo com´ n divisor (MCD) de ambos, que es 12.
                       a            u
El resultado ser´ 5/3.
                ıa
En general, siempre que creamos un nuevo objeto Fraccion, deber´
                                                               ıamos redu-
cirlo dividiendo el numerador y el denominador por el MCD de ambos. Si la
fracci´n ya est´ reducida, el MCD es 1.
      o        a
Euclides de Alejandr´ (aprox. 325–265 a. C.) prensent´ un algoritmo para en-
                    ıa                               o
contrar 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                                                        o

def 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
                                                        o
divisibilidad. En la ultima l´
                     ´       ınea, lo usamos para calcular el resto de la divisi´n.
                                                                                o
Dado que todas las operaciones que hemos escrito creaban un nuevo objeto
Fraccion para devolver el resultado, podemos reducir todos los resultados mo-
dificando el m´todo de inicializaci´n.
             e                    o

class Fraccion:
  def __init__(self, numerador, denominador=1):
    m = mcd (numerador, denominador)
    self.numerador   =   numerador / m
    self.denominador = denominador / m

Ahora siempre que creemos una Fraccion quedar´ reducida a su forma can´ni-
                                             a                        o
ca:
>>> Fraccion(100,-36)
-25/9

Una caracter´
            ıstica estupenda de mcd es que si la fracci´n es negativa, el signo
                                                       o
menos siempre se trasladar´ al numerador.
                          a



B.4.      Comparar fracciones
Supongamos que tenemos dos objetos Fraccion, a y b, y evaluamos a == b. La
implemetaci´n por defecto de == comprueba la igualdad superficial, por lo que
            o
s´lo devuelve true si a y b son el mismo objeto.
 o
Queremos m´s bien devolver verdadero si a y b tienen el mismo valor —eso es,
            a
igualdad 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                                                             o
vez proporcionando un m´todo cmp .
                         e
Por convenio, el m´todo cmp devuelve un n´mero negativo si self es menor
                   e                          u
que otro, zero si son lo mismo, y un n´mero positivo si self es mayor que otro.
                                      u
La forma m´s simple de comparar dos fracciones es la multipicaci´n cruzada.
           a                                                    o
Si 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 datos

class Fraccion:
  ...
  def __cmp__(self, otro):
    dif = (self.numerador * otro.denominador -
           otro.numerador * self.denominador)
    return dif

Si self es mayor que otro, entonces dif ser´ positivo. Si otro is mayor, entonces
                                            a
dif ser´ ngativo. Si son iguales, dif es cero.
       a



B.5.      Forzando la m´quina
                       a
Por supuesto, a´n no hemos terminado. Todav´ hemos de implementar la resta
               u                           ıa
sobrecargando sub y la divisi´n sobrecargando div .
                               o

Una manera de manejar estas operaciones es implementar la negaci´n sobre-
                                                                  o
cargando neg y la inversi´n sobrecargando invert . Entonces podemos
                            o
restar negando el segundo operando y sumando, y podemos dividir invirtiendo
el segundo operando y multiplicando.

Luego, hemos de suministrar los m´todos rsub y rdiv . Desgraciadamen-
                                    e
te, no podemos usar el mismo truco que usamos para la suma y la multiplicaci´n,
                                                                            o
porque la resta y la divisi´n no son conmutativas. No podemos igualar rsub
                           o
y rdiv a sub y div . En estas operaciones, el orden de los operandos
tiene importancia.

Para manejar la negaci´n unitaria, que es el uso del signo menos con un unico
                      o                                                 ´
operando, sobrecargamos el m´todo neg .
                            e

Podemos computar potencias sobrecargando pow , pero la implementaci´n        o
tiene truco. Si el exponente no es un n´mero entero podr´ no ser posible
                                        u                    ıa
representar 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
                                                     u
puede representar como una fracci´n). Por lo tanto, no es f´cil escribir la ver-
                                 o                         a
si´n m´s general de pow .
  o    a

Existe otra extensi´n a la clase Fraccion que cabr´ considerar. Hasta ahora,
                   o                                ıa
hemos asumido que el numerador y el denominador son enteros. Podr´    ıamos
considerar 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                                                            241

B.6.     Glosario
m´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.
                                          o

reducir: Cambiar la fracci´n a su forma equivalente con un MCD igual a 1.
                          o

negaci´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
Todo Sobre El Lenguaje Python
Ap´ndice C
  e

Listados Completos de
Python

C.1.    Clase Punto
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) + ’)’

  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 Hora
class 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 hora



C.3.    Cartas, mazos y juegos
import random

class 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 0

class 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 mano

class 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 cant

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))

    # 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                                 249




C.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 segundo

class 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 + 1



C.5.    Clase Pila

class 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 priorizadas

class 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 carga

class 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 carga

class 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 elemento

class 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 0




C.7.    ´
        Arboles
class 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 = derecha

def total(arbol) :
  if arbol == None : return 0
  return total(arbol.izquierda) + total(arbol.derecha) + arbol.carga

def 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
                          o
def tomaToken(listaToken, esperado):
  if listaToken[0] == esperado:
    listaToken[0:1] = []   # quita el token
    return 1
  else:
C.9 Adivina el animal                                         255

    return 0

def obtieneProducto(listaToken) :
  a = obtieneNumero(listaToken)
  if tomaToken(listaToken, ’*’) :
    b = obtieneProducto(listaToken)
    return Arbol(’*’, a, b)
  else :
    return a

def obtieneSuma(listaToken) :
  a = obtieneProducto(listaToken)
  if tomaToken(listaToken, ’+’) :
    b = obtieneSuma(listaToken)
    return Arbol(’+’, a, b)
  else :
    return a

def 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
                                                               u



C.9.    Adivina el animal
def 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 class
class 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)
Todo Sobre El Lenguaje Python
Ap´ndice D
  e

Lecturas recomendadas

Y 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
                                                                    a
general.

Los ejemplos en este libro han sido deliberadamente simples, por lo que pueden
no haber mostrado las capacidades m´s excitantes de Python. A continuaci´n
                                       a                                    o
exponemos una muestra de las extensiones de Python y sugerencias sobre sus
usos.

     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.
                                a


D.1.      Libros y sitios web sobre Python
Aqu´ 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.
                    a

Y 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
          general
Las siguientes sugerencias sobre lecturas adicionales incluyen muchos de los
libros favoritos de los autores. Estos tratan sobre buenas pr´cticas de progra-
                                                             a
maci´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
  e

GNU Free Documentation
License

Version 1.1, March 2000


Copyright c 2000 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies of this license
document, but changing it is not allowed.



Preamble
The purpose of this License is to make a manual, textbook, or other written do-
cument “free” in the sense of freedom: to assure everyone the effective freedom
to copy and redistribute it, with or without modifying it, either commercially
or noncommercially. Secondarily, this License preserves for the author and pu-
blisher a way to get credit for their work, while not being considered responsible
for modifications made by others.
This License is a kind of “copyleft,” which means that derivative works of the
document must themselves be free in the same sense. It complements the GNU
General Public License, which is a copyleft license designed for free software.
We have designed this License in order to use it for manuals for free software,
because free software needs free documentation: a free program should come
with manuals providing the same freedoms that the software does. But this
264                                     GNU Free Documentation License

License is not limited to software manuals; it can be used for any textual work,
regardless of subject matter or whether it is published as a printed book. We
recommend this License principally for works whose purpose is instruction or
reference.



E.1.      Applicability and Definitions
This License applies to any manual or other work that contains a notice placed
by 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 member
of 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/or
translated into another language.

A “Secondary Section” is a named appendix or a front-matter section of the
Document 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. (For
example, if the Document is in part a textbook of mathematics, a Secondary
Section may not explain any mathematics.) The relationship could be a matter
of 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 the
Document 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 is
released 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, whose
contents can be viewed and edited directly and straightforwardly with generic
text editors or (for images composed of pixels) generic paint programs or (for
drawings) some widely available drawing editor, and that is suitable for input to
text formatters or for automatic translation to a variety of formats suitable for
input to text formatters. A copy made in an otherwise Transparent file format
whose markup has been designed to thwart or discourage subsequent modifica-
tion by readers is not Transparent. A copy that is not “Transparent” is called
“Opaque.”
E.2 Verbatim Copying                                                          265

Examples of suitable formats for Transparent copies include plain ASCII wit-
hout markup, Texinfo input format, L TEX input format, SGML or XML using
                                    A

a publicly available DTD, and standard-conforming simple HTML designed for
human modification. Opaque formats include PostScript, PDF, proprietary for-
mats that can be read and edited only by proprietary word processors, SGML
or XML for which the DTD and/or processing tools are not generally availa-
ble, and the machine-generated HTML produced by some word processors for
output purposes only.
The “Title Page” means, for a printed book, the title page itself, plus such
following pages as are needed to hold, legibly, the material this License requires
to appear in the title page. For works in formats which do not have any title
page as such, “Title Page” means the text near the most prominent appearance
of the work’s title, preceding the beginning of the body of the text.



E.2.      Verbatim Copying
You may copy and distribute the Document in any medium, either commercially
or noncommercially, provided that this License, the copyright notices, and the
license notice saying this License applies to the Document are reproduced in
all copies, and that you add no other conditions whatsoever to those of this
License. You may not use technical measures to obstruct or control the reading
or further copying of the copies you make or distribute. However, you may accept
compensation in exchange for copies. If you distribute a large enough number
of copies you must also follow the conditions in Section 3.
You may also lend copies, under the same conditions stated above, and you may
publicly display copies.



E.3.      Copying in Quantity
If you publish printed copies of the Document numbering more than 100, and
the Document’s license notice requires Cover Texts, you must enclose the copies
in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts
on the front cover, and Back-Cover Texts on the back cover. Both covers must
also clearly and legibly identify you as the publisher of these copies. The front
cover must present the full title with all words of the title equally prominent
and visible. You may add other material on the covers in addition. Copying
with changes limited to the covers, as long as they preserve the title of the
Document and satisfy these conditions, can be treated as verbatim copying in
other respects.
266                                      GNU Free Documentation License

If the required texts for either cover are too voluminous to fit legibly, you should
put the first ones listed (as many as fit reasonably) on the actual cover, and
continue the rest onto adjacent pages.
If you publish or distribute Opaque copies of the Document numbering more
than 100, you must either include a machine-readable Transparent copy along
with each Opaque copy, or state in or with each Opaque copy a publicly acces-
sible computer-network location containing a complete Transparent copy of the
Document, free of added material, which the general network-using public has
access to download anonymously at no charge using public-standard network
protocols. If you use the latter option, you must take reasonably prudent steps,
when you begin distribution of Opaque copies in quantity, to ensure that this
Transparent copy will remain thus accessible at the stated location until at least
one year after the last time you distribute an Opaque copy (directly or through
your agents or retailers) of that edition to the public.
It is requested, but not required, that you contact the authors of the Document
well before redistributing any large number of copies, to give them a chance to
provide you with an updated version of the Document.


E.4.      Modifications
You may copy and distribute a Modified Version of the Document under the
conditions of Sections 2 and 3 above, provided that you release the Modified
Version under precisely this License, with the Modified Version filling the role
of the Document, thus licensing distribution and modification of the Modified
Version to whoever possesses a copy of it. In addition, you must do these things
in 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 that
qualify 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 License

To 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 nothing
but endorsements of your Modified Version by various parties—for example,
statements of peer review or that the text has been approved by an organization
as the authoritative definition of a standard.

You may add a passage of up to five words as a Front-Cover Text, and a passage
of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in
the Modified Version. Only one passage of Front-Cover Text and one of Back-
Cover Text may be added by (or through arrangements made by) any one entity.
If the Document already includes a cover text for the same cover, previously
added by you or by arrangement made by the same entity you are acting on
behalf of, you may not add another; but you may replace the old one, on explicit
permission from the previous publisher that added the old one.

The author(s) and publisher(s) of the Document do not by this License give
permission to use their names for publicity for or to assert or imply endorsement
of any Modified Version.




E.5.      Combining Documents
You may combine the Document with other documents released under this Li-
cense, under the terms defined in Section 4 above for modified versions, provided
that you include in the combination all of the Invariant Sections of all of the
original documents, unmodified, and list them all as Invariant Sections of your
combined work in its license notice.

The combined work need only contain one copy of this License, and multiple
identical Invariant Sections may be replaced with a single copy. If there are
multiple Invariant Sections with the same name but different contents, make
the 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 else
a unique number. Make the same adjustment to the section titles in the list of
Invariant Sections in the license notice of the combined work.

In the combination, you must combine any sections entitled “History” in the
various 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                                                269

E.6.      Collections of Documents
You may make a collection consisting of the Document and other documents
released under this License, and replace the individual copies of this License
in 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 each
of the documents in all other respects.

You may extract a single document from such a collection, and distribute it
individually under this License, provided you insert a copy of this License into
the extracted document, and follow this License in all other respects regarding
verbatim copying of that document.



E.7.      Aggregation with Independent Works
A 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 distribution
medium, does not as a whole count as a Modified Version of the Document,
provided no compilation copyright is claimed for the compilation. Such a com-
pilation is called an “aggregate,” and this License does not apply to the other
self-contained works thus compiled with the Document, on account of their being
thus compiled, if they are not themselves derivative works of the Document.

If the Cover Text requirement of Section 3 is applicable to these copies of the
Document, 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 the
Document within the aggregate. Otherwise they must appear on covers around
the whole aggregate.



E.8.      Translation
Translation is considered a kind of modification, so you may distribute transla-
tions of the Document under the terms of Section 4. Replacing Invariant Sections
with translations requires special permission from their copyright holders, but
you may include translations of some or all Invariant Sections in addition to
the original versions of these Invariant Sections. You may include a translation
of this License provided that you also include the original English version of
this License. In case of a disagreement between the translation and the original
English version of this License, the original English version will prevail.
270                                     GNU Free Documentation License

E.9.      Termination
You may not copy, modify, sublicense, or distribute the Document except as
expressly provided for under this License. Any other attempt to copy, modify,
sublicense, or distribute the Document is void, and will automatically terminate
your rights under this License. However, parties who have received copies, or
rights, from you under this License will not have their licenses terminated so
long as such parties remain in full compliance.



E.10.       Future Revisions of This License
The Free Software Foundation may publish new, revised versions of the GNU
Free Documentation License from time to time. Such new versions will be similar
in spirit to the present version, but may differ in detail to address new problems
or 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 later
version.applies to it, you have the option of following the terms and conditions
either 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 Documents
To use this License in a document you have written, include a copy of the License
in the document and put the following copyright and license notices just after
the 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 271

If you have no Invariant Sections, write “with no Invariant Sections” instead
of 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 for
Back-Cover Texts.
If your document contains nontrivial examples of program code, we recommend
releasing 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.
Todo Sobre El Lenguaje Python
´
Indice alfab´tico
            e

´rbol, 211
a                                     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, 224
a                                     asignaci´n de tuplas, 100, 107, 168
                                               o
´rbol de expresi´n, 213, 216
a                 o                   asignaci´n m´ltiple, 61, 73
                                               o    u
´
ındice, 76, 84, 97, 109, 229          asignmaci´no
     negativo, 76                          tupla, 107
Make Way for Ducklings, 77            atributo, 139
Referencia de la Biblioteca de Pyt-        clase, 163, 170
           hon, 83                    atributo de clase, 163, 170
                                      atributos, 132
, 33
                                      AttributeError, 230
abeced´rico, 77
        a
acceso, 86                            barajar, 167
acto de fe, 55, 186                   base de conocimiento, 221
acumulador, 167, 170, 178             bifurcaci´n condicional, 37
                                                o
aleatorio, 167                        bloque, 37, 45
algoritmo, 10, 146, 148               booleana
alias, 92, 97                              expresi´n, 36
                                                   o
ambig¨edad, 7, 134
       u                              booleanas
     teorema fundamental, 188              funciones, 52
an´lisis sint´ctico, 10
   a         a                        borrado
analizar, 199, 216                         lista, 91
analizar sint´cticamente, 7, 201
              a                       borrado en una lista, 91
andamiaje, 48, 59                     bucle, 63, 73
anidamiento, 45                            anidado, 165
archivo, 128                               condici´n, 228
                                                   o
     texto, 121                            cuerpo, 63, 73
archivo de texto, 121, 128                 en una lista, 187
archivos, 119                              for, 76
argumento, 21, 33                          infinito, 63, 73, 228
argumentos, 28                             recorrido, 76
274                                                ´
                                                   Indice alfab´tico
                                                               e

    variable de, 73               clonar, 97
    while, 62                     codificar, 162, 170
bucle for, 76, 88                 coerci´n, 33
                                         o
bucle infinito, 227, 228                tipo, 115
buffer circular, 210               coerci´n de tipos, 22
                                         o
                                  coercion, 23
c´digo de objeto, 10
 o                                coincidencia de esquemas, 107
c´digo ejecutable, 10
 o
                                  cola, 203, 210
c´digo fuente, 10
 o
                                       implementaci´n con Lista, 203
                                                     o
c´digo muerto, 48, 59
 o
                                       implementaci´n enlazada, 204
                                                     o
cadena, 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, 210
cadena de formato, 123, 128
                                       TAD, 207
cadena inmutable, 79
                                  colecci´n, 185, 196
                                          o
caja, 114
                                  columna, 96
caja de funci´n, 114
               o
car´cter, 75
    a                             coma flotante, 20, 131
carga, 183, 192, 211              comentario, 20
Carta, 161                        comentarios, 19
caso base, 43, 45                 comparable, 164
cifrar, 162                       comparaci´n  o
clase, 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, 20
clase abstracta, 210              concatenation, 18
clase hija, 171, 182              condici´n, 63
                                           o
clase padre, 171, 172, 175, 182   condici´n, 45, 228
                                           o
clase Punto, 155                  condici´n previa, 188, 192
                                           o
clasificaci´n
           o                      condiciones
      car´cter, 82
         a                             encadenadas, 38
clasificaci´n de caracteres, 82
           o                      condiciones encadenadas, 38
clave, 109, 117                   constructor, 131, 139, 162
cliente, 196, 201                 contador, 80, 84
clonado, 93, 112                  conteo, 103
´
Indice alfab´tico
            e                                                           275

conversi´n de tipo, 33
        o                                  m´todos, 111
                                              e
conversi´n de tipos, 22
        o                                  operaciones sobre, 110
copia profunda, 139                   directorio, 125, 128
copia superficial, 139                 dise˜o orientado a objetos, 171
                                          n
copiado, 112, 137                     divisi´n de enteros, 16, 20, 22
                                            o
corresponder, 162                     documentaci´n, 192
                                                   o
cuelgue, 227                          Doyle, Arthur Conan, 6
cuerpo, 37, 45
     bucle, 63                        ejecuci´n
                                              o
cursor, 73                                 flujo, 229
                                      ejecuci´n condicional, 37
                                              o
decrementar, 84                       elemento, 85, 97
definici´n
        o                             eliminaci´n de naipes, 168
                                                o
     circular, 53                     encapsulaci´n, 136, 195, 200
                                                    o
     recursiva, 219                   encapsulado, 67
definici´n circular, 53
        o                             encapsular, 73
definici´n de funci´n, 25, 33
        o           o                 enchapado, 197
definici´n recursiva, 219
        o                             encolamiento priorizado, 203
delimitador, 97, 125, 199, 201        encurtido, 125, 128
denominador, 235                      enlace, 192
depuraci´n, 10, 225
          o                           enteros
depuraci´n (debugging), 4
          o                                divisi´n, 22
                                                  o
desarrollo                                 largos, 115
     incremental, 148                 enteros largos, 115
     planificado, 148                  envoltorio, 192
desarrollo de progamas                error
     encapsulado, 67                       de sintaxis, 225
desarrollo de programas, 73                en tiempo de compilaci´n, 225
                                                                    o
     generalizaci´n, 67
                  o                        en tiempo de ejecuci´n, 43, 225
                                                                o
desarrollo de prototipos, 145              sem´ntico, 225, 231
                                                a
desarrollo incremental, 49, 59, 148        sintaxis, 5
desarrollo incremental de progra-          tiempo de ejecuci´n, 5
                                                              o
           mas, 226                   error (bug), 4
desarrollo planificado, 148            error de sintaxis, 225
desbordamiento, 115                   error en tiempo de compilaci´n, 225
                                                                    o
determinista, 107                     error en tiempo de ejecuci´n, 5, 10,
                                                                  o
diagrama de estado, 13, 20                       43, 76, 79, 87, 100, 111,
diagrama de pila, 33                             113, 115, 120, 124, 225,
diagramas de pila, 30, 42                        229
diccionario, 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), 10
diccionarios, 109                     escalera de color, 174
276                                                    ´
                                                       Indice alfab´tico
                                                                   e

escape                                 funci´n de Fibonacci, 113
                                            o
     secuencia de, 73                  funci´n factorial, 54, 57
                                            o
espacio en blanco, 84                  funci´n gamma, 57
                                            o
estilo funcional de programaci´n, o    funci´n pura, 142, 148
                                            o
           144, 148                    funciones
estructura anidada, 161                     argumentos, 28
estructura de datos                         composici´n, 24, 51
                                                      o
     gen´rica, 196, 197
         e                                  llamadas, 21
     recursiva, 183, 192, 212               par´metros, 28
                                               a
estructura de datos recursiva, 212          recursivas, 42
estructura gen´rica de datos, 196,
                 e                          tuplas como valor de retorno,
           197                                   101
estructura recursiva de datos, 183,    funciones booleanas, 52
           192                         funciones matem´ticas, 23
                                                         a
Euclides, 238
excepci´n, 5, 10, 126, 128, 225, 229
        o                              generalizaci´n, 67
                                                   o
expresi´n, 16, 20, 198
       o                               generalizaci´n, 136, 146
                                                   o
     booleana, 45                      generalizar, 73
     grande y peliaguda, 232           Golfista, 209
expresi´n booleana, 36, 45
       o                               gr´fico de llamadas, 114
                                         a
expresi´n regular, 199
       o                               guardi´n, 59
                                             a
                                       gui´n, 10
                                          o
Fibonacci function, 56                 gui´n bajo, 14
                                          o
FIFO, 203, 210
fila, 96                                herencia, 171, 182
float, 12                               histograma, 106, 107, 116
flujo de ejecuci´n, 27, 33, 229
                 o                     Holmes, Sherlock, 6
formal
     lenguaje, 6                       identidad, 134
forzado de tipo de datos, 115          igualdad, 134
fracci´n, 235
      o                                igualdad profunda, 134, 139
     comparaci´n, 239
                o                      igualdad superficial, 134, 139
     multiplicaci´n, 236
                   o                   implementaci´no
     suma, 237                              Cola, 203
frangoso, 53                           imponer, 160
funci´n, 71
     o                                 impresi´n
                                              o
funci´n join, 96
     o                                      mano de cartas, 174
funci´n split, 96
     o                                      objeto mazo, 166
funci´n, 25, 33, 141, 150
     o                                 imprimir
     booleana, 169                          objeto, 133, 150
     definici´n, 25
             o                         incrementar, 84
     factorial, 54                     IndexError, 230
funci´n booleana, 169
     o                                 indicador, 44, 45
´
Indice alfab´tico
            e                                                        277

infijo, 198, 201                      lenguaje formal, 6, 10
infinito                              lenguaje natural, 6, 10, 134
     bucle, 63                       lenguaje seguro, 5
infio, 213                            Linux, 6
inmutable, 99                        lista, 85, 97, 183
instancia, 133, 136, 139                   anidada, 95, 112
     objeto, 132, 150, 164                 bien construida, 191
instancia de objeto, 164                   bucle, 187
instancia de un objeto, 132, 150           bucle for, 88
instanciaci´n, 132
             o                             como par´metro, 185
                                                     a
instanciar, 139                            de objetos, 165
instrucci´n, 4
          o                                elemento, 86
int, 12                                    enlazada, 183, 192
Intel, 64                                  impresi´n, 185
                                                   o
intercambio, 168                           imprimir hacia atr´s, 186
                                                              a
interfaz, 196, 210                         infinita, 187
interpretar, 2, 10                         longitud, 87
invariante, 191, 192                       modificar, 189
invocar, 117                               mutable, 90
invocar m´todos, 111
            e                              pertenencia, 88
irracional, 240                            porciones, 90
iteraci´n, 61, 62, 73
       o                                   recorrer recursivamente, 186
                                           recorrido, 87, 185
juego
                                     lista anidada, 97, 112
    animales, 221
                                     lista enlazada, 183, 192
juego de los animales, 221
                                     lista infinita, 187
KeyError, 230                        ListaEnlazada, 190
                                     listas
l´gico
 o                                         anidadas, 85, 95
     operador, 36                          clonado, 93
lanzar una excepci´n, 126, 128
                   o                       como par´metros, 94
                                                     a
lenguaje, 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, 21
lenguaje completo, 53                local
lenguaje de alto nivel, 2, 10              variable, 69
lenguaje de bajo nivel, 2, 10        locales
lenguaje de programaci´n, 1
                        o                  variables, 29
lenguaje de programaci´n orientado
                        o            logaritmo, 64
          a objetos, 149, 160        longitud, 87
278                                                   ´
                                                      Indice alfab´tico
                                                                  e

lowercase, 82                         modifcador, 148
                                      modificador, 143
m´ximo com´n divisor, 238, 241
 a            u                       modificar listas, 189
m´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, 166
m´todo append, 166
 e                                    n´mero
                                       u
m´todo ayudante, 190, 192
 e                                         aleatorio, 101
m´todo de inicializaci´n, 154, 160,
 e                      o             n´mero aleatorio, 101
                                       u
          165                         NameError, 229
m´todo de lista, 116
 e                                    natural
m´todo envoltorio, 190
 e                                         lenguaje, 6
m´todos de desarrollo
 e                                    negaci´n, 240
                                             o
    incremental, 49                   negaci´n unitaria, 241
                                             o
m´todos de lista, 166
 e                                    nivel, 211, 224
m´todos sobre diccionarios, 111
 e                                    nodo, 183, 192, 211, 224
m´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, 224
m´dulo copy, 137
 o                                    nodo hoja, 211, 224
m´dulo string, 81, 83
 o                                    nodo padre, 211, 224
m´ltiple
 u                                    nodo ra´ 211, 224
                                               ız,
    asignaci´n, 73
             o                        None, 48, 59
manejar errores, 220                  notaci´n de punto, 111, 151, 154
                                             o
manejar una excepci´n, 126, 128
                      o               nueva l´ınea, 73
manejo de errores, 220                numerador, 235
marco, 42
marco de funci´n, 42
                o                     objeto, 91, 97, 131, 139
matem´ticas
       a                                  mudable, 136
    funciones, 23                     objeto invariante, 191
matriz, 95                            objeto mudable, 136
    dispersa, 112                     objetos
mazo, 165                                 lista de, 165
McCloskey, Robert, 77                 obst´culo al rendiminto, 210
                                          a
mensajes de error, 225                operaciones
mismidad, 134                             con listas, 89
modelo                                operaciones con listas, 89
    mental, 232                       operaciones sobre cadenas, 17
modelo 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, 158
operador binario, 213, 224            pop, 197
operador condicional, 164             porci´n, 78, 84
                                            o
operador corchete, 75                 porciones, 90
operador de formato, 123, 128, 209,   portabilidad, 10
         230                          portable, 2
operador in, 88, 169                  postfijo, 198, 201, 213
operador l´gico, 36
          o                           precedencia, 20, 232
operador m´dulo, 35, 45, 173
            o                         prefijo, 214, 224
operador matem´tico, 236
                 a                    print
operador unitario, 240                     sentencia, 9, 10
operadores                            prioridad, 209
    para listas, 89                   producto, 219
operando, 16, 20                      producto interior, 156, 160
orden, 164                            programa, 10
orden completo, 164                        desarrollo de, 73
orden de evaluaci´n, 232
                  o                   programaci´n orientada a objetos,
                                                   o
orden de las operaciones, 17                     149, 171
orden infijo, 214, 215, 224            prosa, 8
orden parcial, 164                    proveedor, 196, 201
orden postfijo, 214, 215, 224          pseudoaleatorio, 107
orden prefijo, 214, 224                pseudoc´digo, 238
                                               o
                                      push, 197
palabra reservada, 13, 20
palabras reservadas, 14               racional, 235
palo, 161                             rama, 38, 45
papel                                 random, 167
     variable, 188                    randrange, 167
par clave-valor, 109, 117             recorrer, 84, 185, 186, 213
par´metros, 28
   a                                  recorrido, 76, 80, 88, 176, 208, 214
par´metro, 33, 94, 133
   a                                       lista, 87
     lista, 94                        recorrido de lista, 97
pass                                  recorrido eureka, 80
     sentencia, 37                    rect´ngulo, 135
                                          a
patr´n, 80
    o                                 recuento, 116
patr´n computacional, 80
    o                                 recursi´n, 213, 214
                                              o
Pentium, 64                                infinita, 228
280                                                ´
                                                   Indice alfab´tico
                                                               e

recursi´n infinita, 227, 228
        o                          sentencia except, 126, 128
recursividad, 40, 42, 45, 53, 55   sentencia pass, 37
     caso base, 43                 sentencia print, 9, 10, 230
     infinita, 43, 57               sentencia return, 40, 233
recursividad infinita, 43, 45, 57   sentencia try, 126
redimiento, 205                    sentencias
reducir, 238, 241                       bloque, 37
redundancia, 7                          compuestas, 37
referencia, 183                    sentencias compuestas, 37
     incrustada, 183, 192               bloque de sentencias, 37
referencia empotrada, 211               cabecera, 37
referencia incrustada, 183, 192         cuerpo, 37
referencias                        sequencia, 85
     alias, 92                     singleton, 189, 190, 192
reglas de precedencia, 17, 20      sintaxis, 5, 10, 226
reparto de naipes, 173             sobrecarga, 160, 236
repetici´n
         o                              operador, 209
     lista, 89                     sobrecarga de operadores, 156, 160,
runtime error, 76                            164, 209
ruta, 125                          soluci´n de problemas, 10
                                         o
                                   subclase, 171, 175, 182
secuencia, 97                      subexpresi´n, 220
                                               o
secuencia aritm´tica, 66
                 e                 suma, 219
secuencia de escape, 66, 73             fracci´n, 237
                                              o
secuencia geom´trica, 66
                e                  sustituir, 164
seguro
     lenguaje, 5                   t´ctica de encolamiento, 203, 210
                                    a
sem´ntica, 5, 10
    a                              tablas, 64
     error, 5                           dos dimensiones, 66
sentencia, 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                     temporales
sentencia break, 121, 128               variables, 48
sentencia compuesta, 45            teorema
sentencia condicional, 45               fundamental de la ambig¨edad,
                                                                u
sentencia continue, 122, 128                 188
´
Indice alfab´tico
            e                                                           281

teorema fundamental de la am-           uppercase, 82
          big¨edad, 192
              u                         uso de alias, 137
tesis de Turing, 53
tiempo constante, 205, 210              valor, 20, 91, 161
tiempo lineal, 205, 210                 valor de retorno, 21, 33, 47, 59, 136
tipo, 11, 12, 20                        valores
     cadena, 12                              tuplas, 101
     float, 12                           valores de retorno
     int, 12                                 tuplas, 101
tipo abstracto de datos |verTAD,        value, 11
          195                           variable, 12, 20
tipo compuesto de datos, 84                  bucle, 173
tipo 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, 33
tipo de datos compuesto, 75, 131        variable temporal, 232
tipo de datos definido por el usua-      variables
          rio, 131                           locales, 69
tipo de funci´n
              o                         variables locales, 29
     modifcador, 143                    variables temporales, 48
     pura, 142
tipo inmutable, 107                     while
tipo mutable, 107                           sentencia, 62
tipos                                   whitespace, 82
     coerci´n, 22
           o
     comprobaci´n, 57
                 o
     conversi´n, 22
              o
tipos de datos
     enteros largos, 115
     inmutables, 99
     tuplas, 99
token, 199, 201, 216
traceback, 31
traza, 127
traza inversa, 43, 229
try, 128
tupla, 99, 107
tuplas, 101
Turing, Alan, 53
TypeError, 229

unidad, 10
Todo Sobre El Lenguaje Python
Todo Sobre El Lenguaje Python
Todo Sobre El Lenguaje Python
Todo Sobre El Lenguaje Python
Todo Sobre El Lenguaje Python

Más contenido relacionado

DOCX
Trabajo de python
PDF
Introducción a python
PDF
Python
PDF
Programacion orientada a objetos python manuel casado martín - universidad ...
DOCX
Manual de python
PDF
Tutorial de Python - Pyar
PDF
Python para todos
PDF
Tutorial python3
Trabajo de python
Introducción a python
Python
Programacion orientada a objetos python manuel casado martín - universidad ...
Manual de python
Tutorial de Python - Pyar
Python para todos
Tutorial python3

La actualidad más candente (6)

PDF
El tutorial de pythonnnn
PDF
Parallel Python sistemas operativos avanzados
PDF
Introduccion a-python
PDF
INFOSAN Tutorial python3 (1)
PDF
Introducción a Python
El tutorial de pythonnnn
Parallel Python sistemas operativos avanzados
Introduccion a-python
INFOSAN Tutorial python3 (1)
Introducción a Python
Publicidad

Destacado (20)

DOCX
Informe 555 aestable
PDF
Info nivel 1 avanzado
PDF
Programa taller-gratuito-python
PDF
Manual Basico para Encantadores de Serpientes (Python)
PDF
Python (práctica 3)
PDF
Excel ingenieria
PDF
Python (práctica 2)
PDF
Iniciación a python
PDF
Python (práctica 1)
PDF
Cuaderno funcion excel
PDF
Programación de Videojuegos con Python y Pilas (VI)
PDF
107956 excel avanzado
PDF
Programación de Videojuegos con Python y Pilas (II)
PDF
Python - Programación para machos
DOCX
Practicas de excel sena
PDF
Python power 1
PDF
Algoritmos programacion-python
PDF
Python (ejercicios)
PDF
Ejercicios python
PPTX
Escenarios de EXCEL
Informe 555 aestable
Info nivel 1 avanzado
Programa taller-gratuito-python
Manual Basico para Encantadores de Serpientes (Python)
Python (práctica 3)
Excel ingenieria
Python (práctica 2)
Iniciación a python
Python (práctica 1)
Cuaderno funcion excel
Programación de Videojuegos con Python y Pilas (VI)
107956 excel avanzado
Programación de Videojuegos con Python y Pilas (II)
Python - Programación para machos
Practicas de excel sena
Python power 1
Algoritmos programacion-python
Python (ejercicios)
Ejercicios python
Escenarios de EXCEL
Publicidad

Similar a Todo Sobre El Lenguaje Python (20)

PDF
Aprende a pensar como programador con python
DOCX
aprenda-a-pensar-como-un-programador-con-python-convertido.docx
PDF
Intro Python
PDF
Resumen python
PDF
ippython 2024 completo Andrés Marzal y Isabel gracia
PDF
programa curso python 2022.pdf
PDF
Aprender a-programar-con-python
PDF
Intro Python
ODP
Gnu Linux I Educacio
DOCX
Informatica 2 tarea 1 corregida
DOCX
Informatica 2 tarea 1 corregida
PDF
Intro python
PDF
PDF
PYTON.pdf
DOC
Taller netbooks parte_2
PPTX
Software libre y su utilización en la enseñanza
 
PDF
Aprender programando: motivaciones y alternativas
DOC
Ponencia aniei alternativas libres
PDF
Lenguaje de programación Python
DOCX
Documento sin título
Aprende a pensar como programador con python
aprenda-a-pensar-como-un-programador-con-python-convertido.docx
Intro Python
Resumen python
ippython 2024 completo Andrés Marzal y Isabel gracia
programa curso python 2022.pdf
Aprender a-programar-con-python
Intro Python
Gnu Linux I Educacio
Informatica 2 tarea 1 corregida
Informatica 2 tarea 1 corregida
Intro python
PYTON.pdf
Taller netbooks parte_2
Software libre y su utilización en la enseñanza
 
Aprender programando: motivaciones y alternativas
Ponencia aniei alternativas libres
Lenguaje de programación Python
Documento sin título

Último (20)

PDF
Maste clas de estructura metálica y arquitectura
PDF
MANUAL TECNOLOGÍA SER MINISTERIO EDUCACIÓN
PDF
Ronmy José Cañas Zambrano - Potenciando la tecnología en Venezuela.pdf
PDF
programa-de-estudios-2011-guc3ada-para-el-maestro-secundarias-tecnicas-tecnol...
PPTX
sa-cs-82-powerpoint-hardware-y-software_ver_4.pptx
PDF
Influencia-del-uso-de-redes-sociales.pdf
PPTX
ANCASH-CRITERIOS DE EVALUACIÓN-FORMA-10-10 (2).pptx
PDF
ADMINISTRACIÓN DE ARCHIVOS - TICS (SENA).pdf
PPTX
Presentación PASANTIAS AuditorioOO..pptx
PPTX
Curso de generación de energía mediante sistemas solares
PDF
capacitación de aire acondicionado Bgh r 410
PPTX
historia_web de la creacion de un navegador_presentacion.pptx
PPT
introduccion a las_web en el 2025_mejoras.ppt
PDF
TRABAJO DE TECNOLOGIA.pdf...........................
PDF
PRESENTACIÓN GENERAL MIPIG - MODELO INTEGRADO DE PLANEACIÓN
DOCX
Zarate Quispe Alex aldayir aplicaciones de internet .docx
PPTX
IA de Cine - Como MuleSoft y los Agentes estan redefiniendo la realidad
PPT
El-Gobierno-Electrónico-En-El-Estado-Bolivia
PDF
MANUAL de recursos humanos para ODOO.pdf
PPTX
la-historia-de-la-medicina Edna Silva.pptx
Maste clas de estructura metálica y arquitectura
MANUAL TECNOLOGÍA SER MINISTERIO EDUCACIÓN
Ronmy José Cañas Zambrano - Potenciando la tecnología en Venezuela.pdf
programa-de-estudios-2011-guc3ada-para-el-maestro-secundarias-tecnicas-tecnol...
sa-cs-82-powerpoint-hardware-y-software_ver_4.pptx
Influencia-del-uso-de-redes-sociales.pdf
ANCASH-CRITERIOS DE EVALUACIÓN-FORMA-10-10 (2).pptx
ADMINISTRACIÓN DE ARCHIVOS - TICS (SENA).pdf
Presentación PASANTIAS AuditorioOO..pptx
Curso de generación de energía mediante sistemas solares
capacitación de aire acondicionado Bgh r 410
historia_web de la creacion de un navegador_presentacion.pptx
introduccion a las_web en el 2025_mejoras.ppt
TRABAJO DE TECNOLOGIA.pdf...........................
PRESENTACIÓN GENERAL MIPIG - MODELO INTEGRADO DE PLANEACIÓN
Zarate Quispe Alex aldayir aplicaciones de internet .docx
IA de Cine - Como MuleSoft y los Agentes estan redefiniendo la realidad
El-Gobierno-Electrónico-En-El-Estado-Bolivia
MANUAL de recursos humanos para ODOO.pdf
la-historia-de-la-medicina Edna Silva.pptx

Todo Sobre El Lenguaje Python

  • 1. Aprenda a Pensar Como un Programador con Python
  • 3. 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
  • 4. 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. n Green Tea Press 1 Grove St. P.O. Box 812901 Wellesley, MA 02482 Se permite copiar, distribuir, y/o modificar este documento bajo los t´rminos de e la GNU Free Documentation License, Versi´n 1.1 o cualquier versi´n posterior o o publicada por la Free Software Foundation; siendo las Secciones Invariantes “Pr´logo”, “Prefacio”, y “Lista de Colaboradores”, sin texto de cubierta, y o sin texto de contracubierta. Se incluye una copia de la licencia en el ap´ndice e titulado “GNU Free Documentation License”. La GNU Free Documentation License est´ disponible en www.gnu.org o escri- a biendo 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 o fuente L TE A X tiene el efecto de generar una representaci´n independiente del o dispositivo 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 o Texto de C´digo Abierto est´n disponibles en o a http://www.thinkpython.com La composici´n de este libro se realiz´ utilizando L TEX y LyX. Las ilustraciones o o A se hicieron con xfig. Todos ellos son programas gratuitos de c´digo abierto. o Historia de la impresi´n: o Abril 2002: Primera edici´n. o ISBN 0-9716775-0-6
  • 5. Pr´logo o Por David Beazley Como 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 o extremadamente f´cil de usar que en los ultimos a˜os se ha hecho muy popu- a ´ n lar. Desarrollado hace diez a˜os por Guido van Rossum, su sintaxis simple y la n sensaci´n general se deriva en gran parte del ABC, un lenguaje desarrollado en o los 1980s para la ense˜anza. Sin embargo, Python tambi´n se cre´ para resolver n e o problemas reales y presenta una variedad amplia de caracter´ ısticas de lenguajes de programaci´n como C++, Java, Modula-3 y Scheme. Debido a esto, una de o las caracter´ ısticas notables de Python es su atractivo para los desarrolladores profesionales de progamaci´n, cient´ o ıficos, investigadores, artistas, y educadores. A pesar del atractivo de Python para muchas comunidades diferentes, puede que a´n se pregunte “¿por qu´ Python?” o “¿por qu´ ense˜ar programaci´n u e e n o con Python?”No es tarea f´cil responder a estas preguntas, en especial cuando a la opini´n popular est´ del lado de alternativas m´s masoquistas como C++ y o a a Java. Sin embargo, pienso que la respuesta m´s directa es que la progrmaci´n a o en Python es simplemente m´s divertida y m´s productiva. a a Cuando imparto cursos de inform´tica, quiero cubrir conceptos importantes, a hacer 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 o demasiada atenci´n a la abstracci´n matem´tica que hace que los estudiantes o o a se frustren con problemas farragosos relacionados con detalles nimios de la sin- taxis, compilaci´n, y la aplicaci´n de reglas aparentemente arcanas. Aunque o o tal abstraci´n y formalismo son importantes para ingenieros profesionales de la o programaci´n y estudiantes que planean continuar sus estudios de inform´tica, o a decidirse por este enfoque en un curso introductorio s´lo tiene ´xito en hacer o e aburrida la inform´tica. Cuando imparto un curso, no quiero tener un aula de a estudiantes sin inspiraci´n. Quisiera verlos intentando resolver problemas in- o teresantes, explorando ideas diferentes, probando enfoques no convencionales,
  • 6. vi Pr´logo o rompiendo las reglas, y aprendiendo de sus errores. Al hacerlo, no quiero perder la mitad del semestre tratando de sortear problemas con una sintaxis abstru- sa, mensajes de error del compilador incomprensibles, o los varios cientos de maneras que un programa puede generar un error de proteci´n general. o Una 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 a lenguaje interpretado, los principiantes pueden tomar el lenguaje y empezar a hacer 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 o se pueden usar para hacer toda clase de tareas que abarcan desde programaci´n o para web a gr´ficos. Este enfoque pr´ctico es una buena manera de enganchar a a a estudiantes y permite que completen proyectos significativos. Sin embargo, Pyt- hon tambi´n puede servir como una base excelente para intruducir conceptos e importantes de inform´tica. Puesto que Python soporta completamente proce- a dimientos y clases, los estudiantes pueden introducirse gradualmente en temas como abstracci´n procedural, estructuras de datos, y programaci´n orientada a o objetos, que son aplicables a cursos posteriores en Java o C++. Python incluso toma prestada cierta cantidad de caracter´ ısticas de lenguajes de programaci´n o funcionales y puede usarse para intruducir conceptos que pudieran ser cubiertos en 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 2 que puede “avanzar r´pido con mejores resultados”. Aunque estos comentarios a se refieren a sus cursos introductorios, a veces uso Python por estas mismas razones en cursos de inform´tica avanzada en la Universidad de Chicago. En a estos cursos me enfrento constantemente con la desalentadora tarea de cubrir un mont´n de material dif´ en un agotador trimestre de nueve semanas. Aun- o ıcil que es ciertamente posible para m´ infligir mucho dolor y sufrimiento usando un ı lenguaje como C++, he visto a menudo que ese estilo es ineficaz, especialmente cuando el curso se trata de un asunto sin relaci´n apenas con la “programaci´n”. o o Encuentro que usar Python me permite dedicarme m´s al asunto en cuesti´n a o mientras permito a los estudiantes completar proyectos utiles. ´ Aunque Python es todav´ un lenguaje joven y en desarollo, creo que tiene ıa un futuro brillante en la educaci´n. Este libro es un paso importante en esa o direcci´n. o David Beazley Universidad de Chicago Autor de Python Essential Reference
  • 7. Prefacio Por Jeff Elkner Este libro debe su existencia a la colaboraci´n hecha posible por la Internet y o al movimiento de software libre. Sus tres autores, un profesor universitario, un profesor de instituto y un programador profesional, todav´ tienen que conocerse ıa cara 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 esfuerzo para ayudar a mejorar este libro. Creemos que este libro es un testamento a los beneficios y futuras posibilidades de este tipo de colaboraci´n, cuyo marco han establecido Richard Stallman y la o Free Software Foundation. C´mo y por qu´ vine a usar Python o e En 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 o institutos en todo el pa´ la decisi´n de cambiar de lenguaje tuvo un impacto ıs, o directo sobre el curriculum de inform´tica en el Insituto de Yorktown en Ar- a lington, Virgina, donde doy clase. Hasta ese momento, el lenguaje de ense˜anza n era Pascal tanto en nuestro curso de primer a˜o como en el AP. Al seguir con n la pr´ctica anterior de dar a los estudiantes dos a˜os de exposici´n al mismo a n o lenguaje, tomamos la decisi´n de cambiar a C++ en el aula de primer a˜o del o n curso 1997-98 de modo que estar´ ıamos en sinton´ con el cambio del Claustro ıa Escolar para el curso AP del a˜o siguiente. n Dos a˜os m´s tarde, me convenc´ de que C++ era una mala elecci´n para iniciar n a ı o a los estudiantes en la inform´tica. Aunque es un lenguaje de programaci´n a o muy poderoso, tambi´n es extremadamente dif´ de aprender y ense˜ar. Me e ıcil n encontr´ luchando constantemente con la dif´ sintaxis de C++ y sus m´ltiples e ıcil u formas de hacer las cosas, y como consecuencia perd´ muchos estudiantes sin ıa
  • 8. viii Prefacio necesidad. Convencido de que deb´ de haber una elecci´n mejor para el lenguaje ıa o de nuestro curso de primer a˜o, me puse a buscar una alternativa para C++. n Necesitaba un lenguaje que funcionase tanto en las m´quinas de nuestro labo- a ratorio de Linux como en las plataformas Windows y Macintosh que la mayor´ ıa de los estudiantes ten´ en casa. Quer´ que fuera de c´digo abierto, para que ıan ıa o los estudiantes pudieran usarlo en casa sin importar su nivel econ´mico. Quer´ o ıa un lenguaje utilizado por programadores profesionales, y que tuviera una co- munidad activa de desarrolladores a su alrededor. Ten´ que soportar tanto la ıa programaci´n procedural como la orientada a objetos. Y lo m´s importante, o a ten´ que ser f´cil de aprender y de ense˜ar. Cuando investigu´ las opciones con ıa a n e estos obejetivos en mente, Python destac´ como el mejor candidato. o Ped´ a uno de los estudiantes m´s talentosos de Yorktown, Matt Ahrens, que ı a probase Python. En dos meses, no s´lo hab´ aprendido el lenguaje, sino que o ıa escribi´ una aplicaci´n llamada pyTicket que permit´ a nuestro personal infor- o o ıa mar de problemas tecnol´gicos via Web. Sab´ que Matt no pod´ terminar una o ıa ıa aplicaci´n de tal escala en tan poco tiempo con C++, y este logro, combinado o con la positiva valoraci´n de Python por parte de Matt, suger´ que Python era o ıa la soluci´n que buscaba. o Encontrar un libro de texto Una vez decidido a usar Python tanto en mis clases de inform´tica b´sica como a a en el a˜o siguiente, el problema m´s acuciante era la falta de un libro de texto n a disponible. El contenido libre vino al rescate. Anteriormente en ese a˜o, Richard Stallman n me present´ a Allen Downey. Ambos hab´ o ıamos escrito a Richard expresando nuestro inter´s en desarrollar conenidos educativos libres. Allen ya hab´ escrito e ıa un libro de texto de inform´tica de primer a˜o, How to Think Like a Com- a n puter Scientist. Cuando le´ ese libro, supe inmediatamente que quer´ usarlo ı ıa en mi clase. Era el libro de inform´tica m´s claro y pr´ctico que hab´ visto. a a a ıa Pon´ el ´nfasis en los procesos de pensamiento involucrados en la programaci´n ıa e o m´s que en las caracter´ a ısticas de un lenguaje en particular. Su lectura me hizo inmediatamente un maestro mejor. How to Think Like a Computer Scientist no era s´lo un libro excelente, sino que o se public´ bajo la licencia p´blica GNU, lo que significaba que pod´ usarse y o u ıa modificarse libremente para ajustarse a las necesidades de su usuario. Una vez que decid´ usar Python, se me ocurri´ que podr´ traducir la versi´n original ı o ıa o en Java del libro de Allen al nuevo lenguaje. Aunque no hubiera sido capaz de escribir un libro de texto por mi cuenta, tener el libro de Allen para trabajar a
  • 9. ix partir de ´l me hizo posible hacerlo, mostrando al mismo tiempo que el modelo e cooperativo de desarrollo que tan buenos resultados hab´ dado en el software ıa pod´ funcionar tambi´n para el contenido educativo. ıa e El trabajo en este libro durante los dos ultimos a˜os ha sido gratificante para mis ´ n estudiantes y para m´ y mis estudiantes desempe˜aron un importante papel en ı, n el proceso. Como pod´ hacer cambios instant´neos cuando alguien encontraba ıa a un error ortogr´fico o un pasaje dif´ a ıcil, los anim´ a buscar errores en el libro e d´ndoles un punto extra cada vez que hac´ una sugerencia que terminaba a ıan como un cambio en el texto. Esto tuvo el doble beneficio de animarlos a leer el texto con m´s atenci´n y tener el texto revisado en profundidad por sus cr´ a o ıticos m´s importantes: los estudiantes que lo usan para aprender inform´tica. a a Para la segunda mitad del libro, acerca de la programaci´n orientada a objetos, o sab´ que necesitar´ a alguien con m´s experiencia real en programaci´n de ıa ıa a o la que yo ten´ para hacerlo bien. El libro se estanc´ en un estado inacabado ıa o durante buena parte de un a˜o hasta que la comunidad de c´digo abierto de n o nuevo proporcion´ los medios necesarios para su terminaci´n. o o Recib´ un correo electr´nico de Chris Meyers expresando su inter´s en el li- ı o e bro. Chris es un programador profesional que empez´ a impartir un curso de o programaci´n con Python el a˜o pasado en el Colegio de Lane Community, o n en Eugene, Oregon. La perspectiva de impartir el curso llev´ a Chris has- o ta el libro, y empez´ a colaborar con ´l inmediatamente. Hacia el final del o e a˜o escolar hab´ creado un proyecto complementario en nuesto sitio web en n ıa http://www.ibiblio.org/obp llamado Python for Fun y estaba trabajando con algunos de mis estudiantes aventajados como profesor magistral, dirigi´ndo- e les m´s all´ de donde yo pod´ llevarles. a a ıa Presentando la programaci´n con Python o El 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 n estudiantes 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
  • 10. x Prefacio El 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 de Inform´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 a ellos est´n sin duda un poco nerviosos, tras haber o´ que programar compu- a ıdo tadores es algo dif´ de aprender. La versi´n C++ siempre me ha obligado a ıcil o elegir entre dos opciones insatisfactorias: explicar las sentencias #include, void main(), {, y } y arriesgarme a confundir o intimidar a algunos estudiantes desde el principio, o decirles “No te preocupes de todo eso ahora, hablaremos de ello m´s tarde”, y arriesgarme a lo mismo. Los objetivos educativos en este momento a del 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 a el entorno de programaci´n. La programaci´n con Python tiene exactamente lo o o que necesito para hacer estas cosas, y nada m´s. a La comparaci´n del texto explicativo de este programa para cada versi´n del o o libro ilustra mejor lo que esto significa para los estudiantes principiantes. Hay trece p´rrafos de explicaci´n de “¡Hola, mundo!” en la versi´n C++. En la a o o versi´n Python s´lo hay dos. A´n m´s importante: los once p´rrafos que faltan o o u a a no tocan las “grandes ideas” de la programaci´n de computadores, sino las o minucias de la sintaxis de C++. Encontr´ que esto mismo suced´ por todo el e ıa libro. P´rrafos enteros desapareciendo de la versi´n Python del texto porque la a o sintaxis clara de Python los hace innecesarios. El uso de un lenguaje de muy alto nivel como Python permite que el profesor deje para m´s tarde hablar sobre los detalles de bajo nivel de la m´quina hasta a a que los estudiantes tengan el fondo necesario para entender los detalles. De este modo crea la habilidad de poner pedag´gicamente “antes lo primero”. Uno de o los 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. Las variables deben declararse seg´n su tipo en parte porque el tama˜o del lugar al u n que 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
  • 11. xi lo 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 a comprensi´n. En Python una variable es un nombre que se˜ala una cosa. Este o n es un concepto mucho m´s intuitivo para estudiantes principiantes y est´ m´s a a a cerca del significado de “variable” que aprendieron en su clase de matem´ticas. a Este a˜o tuve muchas menos dificultades ense˜ando lo que son las variables que n n en el anterior, y pas´ menos tiempo ayud´ndoles con los problemas derivados e a de su uso. Otro ejemplo de c´mo Python ayuda en la ense˜anza y aprendizaje de la pro- o n gramaci´n es en su sintaxis para las funciones. Mis estudiantes siempre han o tenido una gran dificultad comprendiendo las funciones. El problema principal se centra alrededor de la diferencia entre la definici´n de una funci´n y la llama- o o da a una funci´n, y la distinci´n asociada entre un par´metro y un argumento. o o a Python 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 o estudiantes: “cuando definas una funci´n, empieza con def, seguido del nombre o de 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 a con las llamadas. No hay tipo de retorno, tipos de par´metros, o par´metro por a a referencia y valor de por medio, por lo que ahora soy capaz de ense˜ar funciones n en la mitad de tiempo que antes, con mejor comprensi´n. o El uso de Python ha mejorado la eficacia de nuestro programa de inform´ticaa para todos los estudiantes. Veo un mayor nivel general de ´xito y un menor e nivel de frustraci´n del que experiment´ durante los dos a˜os que ense˜´ C++. o e n ne Avanzo m´s r´pido con mejores resultados. M´s estudiantes terminan el curso a a a con la habilidad de crear programas utiles y con la actitud positiva hacia la ´ experiencia de programaci´n que esto engendra. o Formar una comunidad He recibido correos electr´nicos de todos los rincones del planeta de parte o de gente que usa este libro para aprender o enese˜ar a programar. Ha em- n pezando a surgir una comunidad de usuarios, y muchas personas han contri- buido al proyecto mandando materiales a trav´s del sitio web complementario e http://www.thinkpython.com. Con la publicaci´n de este libro en forma impresa, espero que continue y se o acelere el crecimiento de la comunidad de usuarios. La emergencia de esta co- munidad de usuarios y la posibilidad que sugiere para colaboraciones similares entre educadores han sido para m´ las partes m´s excitantes de trabajar en este ı a proyecto. Trabajando juntos, podemos incrementar la calidad de los materiales
  • 12. xii Prefacio disponibles para nuestro uso y ahorrar un tiempo valioso. Les invito a unirse a nuestra comunidad y espero con impaciencia saber algo de ustedes. Por favor, escriban a los autores a feedback@thinkpython.com. Jeffrey Elkner Escuela Secundaria Yortown Arlington, Virginia
  • 13. Lista de Colaboradores Parafraseando la filosof´ de la Free Software Foundation, este libro es libre ıa como la libre expresi´n, pero no necesariamente gratis como la pizza gratis. o Se hizo realidad a causa de una colaboraci´n que no habr´ sido posible sin o ıa la GNU Free Documentation License. As´ que queremos agradecer a la Free ı Software Foundation por desarrollar esta licencia y, por supuesto, ponerla a nuestra disposici´n. o Tambi´n nos gustar´ dar las gracias a los m´s de cien lectores de aguda vista e ıa a que 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 nuestra gratitud en la forma de una lista de colaboradores. Desgraciadamente, esta listo no est´ completa, pero hacemos lo que podemos para mantenerla actualizada. a Si se toma el tiempo de echar un vistazo a la lista, ver´ que cada una de las a personas que aparecen le ha ahorrado a usted y a los lectores que le sucedan la confusi´n de un error t´cnico o una explicaci´n poco clara simplemente en- o e o vi´ndonos una nota. a Pos imposible que parezca tras tantas correcciones, todav´ puede haber ıa errores en el libro. Si se encontrara con una, esperamos que se tome un minuto para ponerse en contacto con nosotros. La direcci´n de correo es o feedback@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 o quedar 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.
  • 14. 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
  • 15. xv Michael 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 o meHora 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 Perl de Jonah Cohen que, a partir de L TEX genera, el HTML. A Craig T. Snydal est´ poniendo a prueba el texto en un curso en la Uni- a versidad de Drew. Ha contribuido con varias sugerencias y correcciones de importancia. Ian Thomas y sus estudiantes usan el texto en un curso de programaci´n. o Son los primeros en probar los Cap´ ıtulos de la segunda mitad del libro, y han hecho numerosas correcciones y sugerencias. Keith Verheyden envi´ una correcci´n del Cap´ o o ıtulo 3. Peter Winstanley nos hizo saber de un persistente error en nuestro lat´ ın del 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 a de escribir el primer borrador del Cap´ ıtulo sobre diccionarios, propor- cion´ una gu´ continuada en las primeras etapas del libro. o ıa Christoph Zwerschke envi´ varias correcciones y sugerencias pedag´gicas, o o y explic´ la diferencia entre gleich y selbe. o James Mayer envi´ un cargamento de errores tipogr´ficos y ortogr´ficos, o a a incluyendo dos en la lista de colaboradores. Hayden McAfee pill´ una inconsistencia potencialmente confusa entre dos o ejemplos. ´ 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 e errores en la versi´n inglesa. o Tauhidul Hoque y Lex Berezhny crearon las ilustraciones del Cap´ ıtulo 1 y mejoraron muchas de las otras ilustraciones. Dr. Michele Alzetta pill´ un error en el Cap´ o ıtulo 8 y envi´ varios comen- o tarios y sugerencias pedag´gicas interesantes sobre Fibonacci y La Mona. o
  • 16. 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
  • 17. ´ Indice general Pr´logo o v Prefacio vii Lista de Colaboradores xiii 1. 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2. 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
  • 18. 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3. 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 4. 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
  • 19. ´ 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 5. 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 6. 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
  • 20. xx ´ Indice general 7. 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 8. 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
  • 21. ´ Indice general xxi 8.15. Matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 8.16. Cadenas y listas . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 8.17. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 9. 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 10.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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 11.Archivos y excepciones 119 11.1. Archivos de texto . . . . . . . . . . . . . . . . . . . . . . . . . . 121 11.2. Escribir variables . . . . . . . . . . . . . . . . . . . . . . . . . . 123 11.3. Directorios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
  • 22. xxii ´ Indice general 11.4. Encurtido . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 11.5. Excepciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126 11.6. Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 12.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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 13.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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 14.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
  • 23. ´ 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 15.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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 16.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
  • 24. xxiv ´ Indice general 17.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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 18.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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201 19.Colas 203 19.1. El TAD Cola . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203 19.2. Cola Enlazada . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204 19.3. Rendimiento t´ ıpico . . . . . . . . . . . . . . . . . . . . . . . . . 205
  • 25. ´ 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224 A. 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 a B. 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
  • 26. xxvi ´ Indice general C. 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . 256 D. Lecturas recomendadas 259 D.1. Libros y sitios web sobre Python . . . . . . . . . . . . . . . . . 260 D.2. Libros recomendados sobre inform´tica en general . . . . . . . . 261 a E. 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
  • 27. Cap´ ıtulo 1 El Camino del Programa El objetivo de este libro es ense˜arle a pensar como lo hacen los cient´ n ıficos inform´ticos. Esta manera de pensar combina las mejores caracter´ a ısticas de la matem´tica, la ingenier´ y las ciencias naturales. Como los matem´ticos, los a ıa, a cient´ ıficos inform´ticos usan lenguajes formales para designar ideas (espec´ a ıfica- mente, computaciones). Como los ingenieros, ellos dise˜an cosas, ensamblando n sistemas a partir de componentes y evaluando ventajas y desventajas de cada una de las alternativas. Como los cient´ ıficos, ellos observan el comportamiento de sistemas complejos, forman hip´tesis, y prueban sus predicciones. o La habilidad m´s importante del cient´ a ıfico inform´tico es la soluci´n de pro- a o blemas. La soluci´n de problemas incluye poder formular problemas, pensar en o la soluci´n de manera creativa, y expresar una soluci´n con claridad y precisi´n. o o o Como se ver´, el proceso de aprender a programar es la oportunidad perfecta a para desarrollar la habilidad de resolver problemas. Por esa raz´n este cap´ o ıtulo se 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 u resultado. Ese resultado se ver´ m´s claramente durante el proceso. a a 1.1. El lenguaje de programaci´n Python o El lenguaje de programaci´n que aprender´ es Python. Python es un ejemplar o a de un lenguaje de alto nivel; otros ejemplos de lenguajes de alto nivel son C, C++, Perl y Java.
  • 28. 2 El Camino del Programa Como se puede deducir de la nomenclatura “lenguaje de alto nivel”, tambi´n e existen lenguajes de bajo nivel, a los que tambi´n se califica como lengua- e jes de m´quina o lenguajes ensambladores. A prop´sito, los computadores s´lo a o o ejecutan programas escritos en lenguajes de bajo nivel. Los programas de alto nivel tienen que traducirse antes de ejecutarse. Esta traducci´n lleva tiempo, lo o cual es una peque˜a desventaja de los lenguajes de alto nivel. n Aun as´ las ventajas son enormes. En primer lugar, la programaci´n en lenguajes ı o de alto nivel es mucho m´s f´cil; escribir programas en un lenguaje de alto nivel a a toma menos tiempo, los programas son m´s cortos y m´s f´ciles de leer, y es m´s a a a a probable que estos programas sean correctos. En segundo lugar, los lenguajes de alto nivel son portables, lo que significa que pueden ejecutarse en tipos diferentes de computadores sin modificaci´n alguna o con pocas modificaciones. o Los programas escritos en lenguajes de bajo nivel s´lo pueden ser ejecutarse en o un tipo de computador y deben reescribirse para ejecutarlos en otro. Debido a estas ventajas, casi todos los programa se escriben en un lenguaje de alto nivel. Los lenguajes de bajo nivel s´lo se usan para unas pocas aplicaciones o especiales. Hay dos tipos de programas que traducen lenguajes de alto nivel a lenguajes de bajo nivel: int´rpretes y compiladores. Un int´rprete lee un programa de e e alto 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 FUENTE Un compilador lee el programa y lo traduce todo al mismo tiempo, antes de ejecutar cualquiera de las instrucciones. En este caso, al programa de alto nivel se le llama el c´digo fuente, y al programa traducido el c´digo de obje- o o to o el c´digo ejecutable. Una vez compilado el programa, puede ejecutarlo o repetidamente sin volver a traducirlo. CODIGO CODIGO COMPILADOR EJECUTOR SALIDA FUENTE OBJETO Python 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
  • 29. 1.2 ¿Qu´ es un programa? e 3 int´rprete: modo de comando y modo de gui´n. En modo de comando se escri- e o ben sentencias en el lenguaje Python y el int´rprete muestra el resultado. e $ python Python 1.5.2 (#1, Feb 1 2000, 16:32:16) Copyright 1991-1995 Stichting Mathematish Centrum, Amsterdam >>> print 1 + 1 2 La primera l´ ınea de este ejemplo es el comando que pone en marcha el int´rprete e Python. Las dos l´ıneas siguientes son mensajes del int´rprete. La tercera l´ e ınea comienza con >>>, que es la invitaci´n del int´rprete para indicar que est´ listo. o e a Escribimos print 1 + 1 y el int´rprete contest´ 2. e o Alternativamente, se puede escribir el programa en un archivo y usar el int´rpre- e te 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 o latoya.py que contenga esta l´ ınea: print 1 + 1 Por acuerdo un´nime, los archivos que contienen programas de Python tienen a nombres que terminan con .py. Para ejecutar el programa, se le tiene que indicar el nombre del gui´n al int´rpre- o e te. $ python latoya.py 2 En otros entornos de desarrollo los detalles de la ejecuci´n de programas pueden o ser diferentes. Aem´s, la mayor´ de programas son m´s interesantes que el a ıa a mencionado. 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 para pruebas r´pidas porque se pueden teclear las instrucciones de Python y se pue- a den ejecutar inmediatamente. Una vez que un programa est´ completo, puede a archivarse en un gui´n para ejecutarlo o modificarlo en el futuro. o 1.2. ¿Qu´ es un programa? e Un programa es una secuencia de instrucciones que especifican c´mo ejecutar o una computaci´n. La computaci´n puede ser algo matem´tico, como solucionar o o a
  • 30. 4 El Camino del Programa un sistema de ecuaciones o determinar las ra´ de un polinomio, pero tambi´n ıces e puede ser una computaci´n simb´lica, como buscar y reemplazar el texto de un o o documento o (aunque parezca raro) compilar un programa. Las instrucciones (comandos, ´rdenes) tienen una apariencia diferente en len- o guajes de programaci´n diferentes, pero existen algunas funciones b´sicas que o a se 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. o operaci´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. o Lo crea o no, eso es todo. Todos los programas que existen, por complicados que sean, 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 o vez m´s peque˜as hasta que estas tareas sean suficientemente simples para ser a n ejecutadas con una de estas instrucciones simples. Quiz´s esta descripci´n sea un poco ambigua. No se preocupe. Lo explicaremos a o con m´s detalle con el tema de los algoritmos. a 1.3. ¿Qu´ es la depuraci´n (debugging)? e o La programaci´n es un proceso complejo y, por ser realizado por humanos, a o menudo desemboca en errores. Por razones caprichosas, esos errores se llaman bugs 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, en tiempo de ejecuci´n y sem´nticos. Es muy util distinguirlos para encontrarlos o a ´ mas r´pido. a
  • 31. 1.3 ¿Qu´ es la depuraci´n (debugging)? e o 5 1.3.1. Errores sint´cticos a Python s´lo puede ejecutar un programa si el programa es correcto sint´ctica- o a mente. En caso contrario, es decir si el programa no es correcto sint´cticamente, a el proceso falla y devuelve un mensaje de error. El t´rmino sintaxis se refiere a e la 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 u oraciones deben terminar con un punto. esta oraci´n tiene un error sint´ctico. o a Esta oraci´n tambi´n o e Para la mayor´ de lectores, unos pocos errores sint´cticos no son significatvos, ıa a y por eso pueden leer la poes´ de e. e. cummings sin anunciar errores de sin- ıa taxis. Python no es tan permisivo. Si hay aunque sea un solo error sint´ctico a en el programa, Python mostrar´ un mensaje de error y abortar´ la ejecuci´n a a o del programa. Durante las primeras semanas de su carrera como programador pasar´, seguramente, mucho tiempo buscando errores sint´cticos. Sin embargo, a a tal como adquiera experiencia tendr´ menos errores y los encontrar´ mas r´pido. a a a 1.3.2. Errores en tiempo de ejecuci´n o El segundo tipo de error es un error en tiempo de ejecuci´n. Este error no aparece o hasta que se ejecuta el programa. Estos errores tambi´n se llaman excepciones e porque indican que algo excepcional (y malo) ha ocurrido. Con los programas que vamos a escribir al principio, los errores en tiempo de ejecuci´n ocurrir´n con poca frecuencia, as´ que puede pasar bastante tiempo o a ı hasta que vea uno. 1.3.3. Errores sem´nticos a El tercer tipo de error es el error sem´ntico. Si hay un error de l´gica en su a o programa, el programa se ejecutar´ sin ning´n mensaje de error, pero el resul- a u tado no ser´ el deseado. Ser´ cualquier otra cosa. Concretamente, el programa a a har´ lo que usted le dijo. a A veces ocurre que el programa escrito no es el programa que se ten´ en mente. ıa El sentido o significado del programa (su sem´ntica) no es correcto. Es dif´ ha- a ıcil llar errores de l´gica, porque requiere trabajar al rev´s, observando el resultado o e del programa para averiguar lo que hace.
  • 32. 6 El Camino del Programa 1.3.4. Depuraci´n experimental o Una de las t´cnicas m´s importantes que usted aprender´ es la depuraci´n. Aun- e a a o que a veces es frustrante, la depuraci´n es una de las partes m´s intelectualmente o a ricas, interesantes y estimulantes de la programaci´n. o La depuraci´n es una actividad parecida a la tarea de un investigador: se tie- o nen que estudiar las claves para inducir los procesos y eventos llevaron a los resultados que tiene a la vista. La depuraci´n tambi´n es una ciencia experimental. Una vez que se tiene la o e idea de cu´l es el error, se modifica el programa y se intenta nuevamente. Si su a hip´tesis fue la correcta se pueden predecir los resultados de la modificaci´n y o o estar´ m´s cerca de un programa correcto. Si su hip´tesis fue err´nea tendr´ que a a o o a idearse otra hip´tesis. Como dijo Sherlock Holmes, “Cuando se ha descartado o lo 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 o gramaci´n es el proceso de depurar un programa gradualmente hasta que haga o lo que usted quiera. La idea es que deber´ usted comenzar con un programa ıa que haga algo y hacer peque˜as modificaciones, depur´ndolas sobre la marcha, n a de modo que siempre tenga un programa que funcione. Por ejemplo, Linux es un sistema operativo que contiee miles de l´ ıneas de c´digo, o pero Linus Torvalds lo comenz´ como un programa para explorar el microproce- o sador Intel 80836. Seg´n Larry Greenfield, “Uno de los proyectos tempranos de u Linus fue un programa que alternaba la impresi´n de AAAA con BBBB. Este o programa evolucion´ en Linux” (de The Linux Users’Guide Versi´n Beta 1). o o Otros cap´ ıtulos tratar´n m´s acerca del tema de depuraci´n y otras t´cnicas de a a o e programaci´n. o 1.4. Lenguajes formales y lenguajes naturales Los lenguajes naturales son los lenguajes hablados por seres humanos, como el espa˜ol, el ingl´s y el franc´s. No los han dise˜ados personas (aunque se n e e n intente poner cierto orden en ellos), sino que se han desarrollado naturalmente. Los lenguajes formales son lenguajes dise˜ados por humanos y que tienen n aplicaciones espec´ıficas. La notaci´n matem´tica, por ejemplo, es un lenguaje o a formal ya que se presta a la representaci´n de las relaciones entre n´meros y o u s´ ımbolos. Los qu´ımicos utilizan un lenguaje formal para representar la estructura qu´ımica de las mol´culas. Y lo m´s importante: e a
  • 33. 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- a plo, 3 + 3 = 6 es una expresi´n matem´tica correcta, pero 3 = +6$ no lo es. De o a la misma manera, H2 0 es una nomenclatura qu´ ımica correcta, pero 2 Zz no lo es. Existen dos clases de reglas sint´cticas, en cuanto a unidades y estructura. Las a unidades son los elementos b´sicos de un lenguaje, como lo son las palabras, los a n´meros y los elementos qu´ u ımicos. Por ejemplo, en 3=+6$, $ no es una unidad matem´tica aceptada (al menos hasta donde nosotros sabemos. Similarmente, a 2 Zz no es formal porque no hay ning´ n elemento con la abreviatura Zz. u La segunda clase de regla sint´ctica est´ relacionada con la estructura de un a a elemento; 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 el s´ o n´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. a Al leer una oraci´n, sea en un lenguaje natural o una sentencia en un lenguaje o t´cnico, se debe discernir la estructura de la oraci´n. En un lenguaje natural e o este proceso, llamado an´lisis sint´ctico ocurre subconscientemente. a a Por ejemplo cuando usted escucha la oraci´n “El otro zapato cay´”, entiende o o que “el otro zapato” es el sujeto y “cay´” es el verbo. Cuando se ha analizado o la oraci´n sint´cticamente, se puede deducir el significado, o la sem´ntica, de la o a a oraci´n. Suponiendo que sepa lo ques es un zapato y lo que es caer, entender´ el o a significado de la oraci´n. o Aunque existen muchas cosas en com´n entre los lenguajes naturales y los u lenguajes formales—por ejemplo las unidades, la estructura, la sintaxis y la sem´ntica—tambi´n existen muchas diferencias: a e ambig¨ 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
  • 34. 8 El Camino del Programa redundancia: 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. a literalidad: 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 veces la diferencia entre los lenguajes formales y los naturales es comparable a la diferencia 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 o Prosa: 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 u Programas: 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 a que los lenguajes naturales, y por consecuente lleva m´s tiempo leerlos. Tam- a bi´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 la estructura. Finalmente, ponga atenci´n a los detalles. Los fallos de puntuaci´n o o y la ortograf´ que puede obviar en el lenguaje natural, pueden suponer una ıa, gran diferencia en un lenguaje formal. 1.5. El primer programa Tradicionalmente el primer programa en un lenguaje nuevo se llama “Hola, mundo” (Hello world!) porque s´lo muestra las palabras “Hola a todo el mundo”. o En Python es as´ ı: print "Hola, mundo"
  • 35. 1.6 Glosario 9 Este es un ejemplo de una sentencia print, la cual no imprime nada en papel, m´s bien muestra un valor. En este caso, el resultado es las palabras a Hola, mundo Las comillas se˜alan el comienzo y el final del valor; no aparecen en el resultado. n Alguna gente eval´a la calidad de un lenguaje de programaci´n por la simplici- u o dad del programa “Hola, mundo”. Si seguimos ese criterio, Python cumple con todas sus metas. 1.6. Glosario soluci´n de problemas: El proceso de formular un problema, hallar la solu- o ci´n y expresar esa soluci´n. o o lenguaje 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. a interpretar: Ejecutar un programa escrito en un lenguaje de alto nivel tradu- ci´ndolo l´ e ınea por l´ ınea compilar: 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). o programa: Un conjunto de instrucciones que especifica una computaci´n. o algoritmo: Un proceso general para resolver una clase completa de problemas.
  • 36. 10 El Camino del Programa error (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. o sintaxis: 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). a error 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. u excepci´n: Otro nombre para un error en tiempo de ejecuci´n. o o error 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. a language 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. a an´lisis sint´ctico: La examinaci´n de un programa y el an´lisis de su estruc- a a o a tura sint´ctica. a sentencia print: Una instrucci´n que causa que el int´rprete Python muestre o e un valor en el monitor.
  • 37. Cap´ ıtulo 2 Variables, expresiones y sentencias 2.1. Valores y tipos El valor es uno de los elementos fundamentales (como por ejemplo una letra o un n´mero) que manipula un programa. Los valores que hemos visto hasta el u momento 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 una cadena, llamada as´ porque contiene una “cadena” de letras. Usted (y el ı int´rprete) puede identificar las cadenas porque est´n encerradas entre comi- e a llas. La sentencia print tambi´n funciona con enteros: e >>> print 4 4 Si no est´ seguro del tipo que tiene un determinado valor, puede pregunt´rselo a a al 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 e los enteros sean de tipo int (por integer en ingl´s). De forma menos obvia, los e
  • 38. 12 Variables, expresiones y sentencias n´meros con decimales (separados por medio de un punto en ingl´s) son de tipo u e float debido a la representaci´n de estos n´meros en el formato llamado de o u coma 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 a entre 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 ıa de tres d´ ´ ıgitos, como en 1,000,000. Este no es un entero legal en Python, pero es una expresi´n legal: o >>> print 1,000,000 1 0 0 En fin, no era eso lo que quer´ ıamos. Python interpreta 1,000,000 como una lista de tres n´meros que debe imprimir. As´ que recuerde no insertar comas en u ı sus enteros. 1 2.2. Variables Una de las caracter´ ısticas m´s potentes de los lenguajes de programaci´n es a o la capacidad de manipular variables. Una variable es un nombre que hace referencia a un valor. La sentencia de asignaci´n crea nuevas variables y les asigna un valor: o >>> mensaje = "Que onda?" >>> n = 17 >>> pi = 3.14159 Este 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 n apunt´ en una nota anterior o
  • 39. 2.3 Nombres de variables y palabras reservadas 13 valor entero 17 a n, y la tercera le da el valor de n´mero en coma flotante u 3.14159 a pi. Una forma habitual de representar variables sobre el papel es escribir el nombre con una flecha se˜alando al valor de la variable. Este tipo de representaci´n n o se llama diagrama de estado, ya que muestra en qu´ estado se halla cada e una de las variables (consid´relo como el “estado de ´nimo” de la variable”). e a El siguiente diagrama muestra el efecto de las tres sentencias de asignaci´n o anteriores: mensaje "Que onda?" n 17 pi 3.14159 La sentencia print tambi´n funciona con variables. e >>> print mensaje "Que onda?" >>> print n 17 >>> print pi 3.14159 En cada caso, el resultado es el valor de la variable. Las variables tambi´n tienen e tipo. 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 reservadas Como norma general, los programadores eligen nombres significativos para sus variables: esto permite documentar para qu´ se usa la variable. e Los nombres de las variables pueden tener una longitud arbitraria. Pueden estar formados por letras y n´meros, pero deben comenzar con una letra. Aunque es u
  • 40. 14 Variables, expresiones y sentencias aceptable usar may´sculas, por convenci´n no lo hacemos. Si lo hace, recuerde u o que la distinci´n es importante: Bruno y bruno son dos variables diferentes. o El gui´n bajo ( ) tambi´n es legal y se utiliza a menudo para separar nombres o e con m´ltiples palabras, como mi nombre o precio del cafe colombiano. u Si intenta darle a una variable un nombre ilegal, obtendr´ un error de sintaxis. a >>> 76trombones = "gran desfile" SyntaxError: invalid syntax >>> mas$ = 1000000 SyntaxError: invalid syntax >>> class = "Curso de Programaci´n 101" o SyntaxError: invalid syntax 76trombones es ilegal porque no comienza por una letra. mas$ es ilegal porque contiene un car´cter ilegal, el signo del d´lar. Pero ¿qu´ tiene de malo class? a o e Resulta que class es una de las palabras reservadas de Python. El lenguaje usa las palabras reservadas para definir sus reglas y estructura, y no pueden usarse 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 while Tal vez quiera mantener esta lista a mano. Si el int´rprete se queja de alguno e de sus nombres de variable, y usted no sabe por qu´, compruebe si est´ en esta e a lista. 2.4. Sentencias Una sentencia es una instrucci´n que puede ejecutar el int´rprete de Python. o e Hemos visto dos tipos de sentencias: print y la asignaci´n. o Cuando usted escribe una sentencia en la l´ ınea de comandos, Python la ejecuta y 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 u Normalmente un gui´n contiene una secuencia de sentencias. Si hay m´s de una o a sentencia, los resultados aparecen de uno en uno tal como se van ejecutando las sentencias.
  • 41. 2.5 Evaluar expresiones 15 Por ejemplo, el gui´n o print 1 x = 2 print x prsenta la salida 1 2 De nuevo, la sentencia de asignaci´n no produce ninguna salida. o 2.5. Evaluar expresiones Una expresi´n es una combinaci´n de valroes, variables y operadores. Si teclea o o una expresi´n en la l´ o ınea de comandos, el int´rprete la eval´ a y muestra el e u resultado: >>> 1 + 1 2 Un valor, y tambi´n una variable, se considera una expresi´n por s´ mismo. e o ı >>> 17 17 >>> x 2 Para complicar las cosas, evaluar una expresi´n no es del todo lo mismo que o imprimir un valor. >>> mensaje = "Que onda?" >>> mensaje "Que onda?" >>> print mensaje Que onda? Cuando Python muestra el valor de una expresi´n, usa el mismo formato que o usted usar´ para introducir un valor. En el caso de las cadenas, eso significa que ıa incluye las comillas. Pero la sentencia print imprime el valor de la expresi´n, lo o que 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 a El gui´n o
  • 42. 16 Variables, expresiones y sentencias 17 3.2 "Hola, mundo" 1 + 1 no presenta ninguna salida. ¿C´mo cambiar´ usted el gui´n para mostrar los o ıa o valores de estas cuatro expresiones? 2.6. Operadores y expresiones Los operadores son s´ ımbolos especiales que representan c´lculos simples, como a la suma y la multiplicaci´n. Los valores que usa el operador se llaman operan- o dos. Las siguientes expresione son legales en Python y su significado es m´s o menos a claro: 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 e todos de la misma forma que en matem´ticas. El asterisco (*) es el signo de a multiplicaci´n y ** el s´ o ımbolo para exponenciaci´n. o Cuando aparece el nombre de una variable en el lugar de un operando, se sus- tituye con su valor antes de realizar la operaci´n. o La suma, resta, multiplicaci´n y exponenciaci´n hacen lo esperado, pero la divi- o o si´n le puede sorprender. La operaci´n que sigue tiene un resultado inesperado: o o >>> minuto = 59 >>> minuto/60 0 El valor de la variable minuto es 59, y 59 dividido entre 60 es 0.98333 y no 0. El motivo de la discrepancia reside en que Python est´ llevando a cabo una a divisi´n de enteros. o Cuando ambos operandos son enteros, el resultado ha de ser tambi´n un entero; e por convenci´n, la divisi´n de enterios simpre se redondea a la baja, incluso en o o casos como estos en los que el siguiente entero est´ muy pr´ximo. a o Una alternativa posible en este caso es el c´lculo de un porcentaje y no el de a una fracci´n: o >>> minuto*100/60 98
  • 43. 2.7 El orden de las operaciones 17 De nuevo se redondea el resultado a la baja, pero al menos ahora la respuesta es aproximadamente correcta. Otra alternativa es la divisi´n de coma flotante, o que veremos en el Cap´ ıtulo 3. 2.7. El orden de las operaciones Cuando aparece m´s de un operador en una expresi´n, el orden de evaluaci´n a o o depende de las reglas de precedencia. Python sigue las mismas reglas de precedencia que los propios matem´ticos para sus operaciones matem´ticas. a a Los ingleses usan el acr´nimo PEMDAS como regla parea recordar el orden de o las 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. ıa 2.8. Las operaciones sobre cadenas En general no es posible realizar operaciones matem´ticas con cadenas, incluso si a las cadenas parecen n´meros. Las siguientes sentencias son ilegales (suponiendo u que mensaje sea de tipo string) mensaje-1 "Hola"/123 mensaje*"Hola" "15"+2
  • 44. 18 Variables, expresiones y sentencias Es curioso que el operador + funcione con cadenas, aunque no haga exactamente lo 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 e extremo. Por ejemplo: fruta = "pl´tano" a bizcochoBueno = " pan de leche" print fruta + bizcochoBueno La salida del programa es pl´tano pan de leche. El espacio delante de pan a es parte de la cadena, y es necesario para introducir el espacio que separa las cadenas concatenadas. El operador * tambi´n funciona con cadenas; lleva a cabo la repetici´n. Por e o ejemplo ’Chiste’*3 es ’ChisteChisteChiste’. Uno de los operandos ha de ser una cadena, el otro ha de ser un entero. Por un lado, esta interpretaci´n de + y * cobra sentido por analog´ con la o ıa suma y la multimplicaci´n. Igual que 4*3 es equivalente a 4+4+4, esperamos o que ’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 o de la adici´n y la multiplicaci´n de enteros. ¿Puede encontrar una propiedad que o o tienen la suma y la multiplicaci´n de enteros y que no tengan la concatenaci´n o o y la repetici´n de cadenas? o 2.9. Composici´n o Hasta ahora hemos examinado los elementos de un programa (variables, expre- siones y sentencias) por separado, sin hablar de c´mo combinarlos. o Una de las caracter´ısticas m´s utiles de los lenguajes de programaci´n es su a ´ o capacidad de tomar peque˜os bloques de construcci´n y ensamblarlos. Por n o ejemplo, sabemos c´mo sumar n´meros y c´mo imprimirlos; resulta que pode- o u o mos hacer las dos cosas a un tiempo: >>> print 17 + 3 20 En realidad, no debemos decir “al mismo tiempo”, puesto que en realidad la suma tiene que realizarse antes de la impresi´n, pero la cuesti´n es que cualquier o o expresi´n relacionada con n´meros, cadenas y variables puede usarse dentro de o u una sentencia print. Ya hemos visto un ejemplo de ello: print "N´mero de minutos desde la medianoche: ", hora*60+minuto u
  • 45. 2.10 Los comentarios 19 Y tambi´n puede poner expresiones arbitrarias en la parte derecha de una sen- e tencia de asignaci´n: o porcentaje = (minuto * 100) / 60 Esta capacidad puede no resultar muy sorprendente, pero ya veremos otros ejemplos donde la composici´n hace posible expresar c´lculos complejos con o a limpieza y brevedad. ´ ATENCION: Hay l´ ımites al lugar donde pueden usarse ciertas expresiones. Por ejemplo, la parte izquierda de una sentencia de asignaci´n tiene que ser un o nombre de variable, no una exrpresi´n. Por tanto es ilegal lo siguiente: minute+1 o = hour. 2.10. Los comentarios Conforme los programas van creciendo de tama˜o y complic´ndose, se vuelven n a m´s complicados de leer. Los lenguajes formales son densos y con frecuencia es a dif´ observar un trozo de c´digo y averiguar lo que hace, o por qu´ lo hace. ıcil o e Por ello es una buena idea a˜adir notas a su programa que expliquen, en un n lenguaje natural, qu´ hace el programa. Estas notas se llaman comentarios y e se marcan con el s´ ımbolo #: # calcula el porcentaje de la hora que ha pasado ya porcentaje = (minuto * 100) / 60 En este caso, el comentario aparece en una l´ ınea propia. Tambi´n puede poner e comentarios al final de otras l´ ıneas: porcentaje = (minuto * 100) / 60 # ojo: divisi´n de enteros o Todo lo que va del # al final de la l´ınea se ignora (no tiene efecto sobre el programa). El mensaje est´ destinado al programador, o a futuros programa- a dores que podr´ tener que usar el c´digo. En este caso avisa al lector sobre el ıan o sorprendente comportamiento de la divisi´n de enteros. o 2.11. Glosario valor: 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
  • 46. 20 Variables, expresiones y sentencias tipo: 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). u coma flotante: un formato para representar n´meros con decimales. u variable: 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. o diagrama 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 o operando: uno de los valores sobre los que act´a un operador. u expresi´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. u concatenar: 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. a comentario: 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
  • 47. Cap´ ıtulo 3 Funciones 3.1. Llamadas a funciones Ya 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. o El valor o variable, llamado el argumento de la funci´n, ha de estar encerrado o entre 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 o y devuelve un entero que act´a como identificador unico de ese valor. u ´ >>> id(3) 134882108 >>> yanira = 3 >>> id(yanira) 134882108 Cada valor tiene un id, que es un valor unico relacionado con d´nde se almacena ´ o en la memoria del computador. El id de una variable es el id del valor al que hace referencia.
  • 48. 22 Funciones 3.2. Conversi´n de tipos o Python proporciona una colecci´n de funciones internas que convierten valores o de un tipo a otro. La funci´n int toma un valor y lo convierte a un entero, si o es posible, o da un error si no es posible. >>> int("32") 32 >>> int("Hola") ValueError: invalid literal for int(): Hola int tambi´n convierte valores de coma flotante a enteros, pero recuerde que e siempre redondea hacia abajo: >>> int(3.99999) 3 La funci´n float que convierte enteros y cadenas en n´meros en coma flotante: o u >>> float(32) 32.0 >>> float("3.14159") 3.14159 Finalmente, 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 n de coma flotante 1.0. Tal vez representen el mismo n´mero, pero pertenecen u a tipos distintos. El motivo es que se representan de forma distinta dentro del computador. 3.3. Coerci´n de tipos o Ahora que ya sabemos convertir entre tipos, tenemos otra forma de enfrentarnos a la divisi´n de enteros. Volviendo al ejemplo del cap´ o ıtulo anterior, suponga que queremos calcular qu´ fracci´n de una hora hab´ transcurrido. La expresi´n e o ıa o m´s obvia, minuto / 60, realiza una divisi´n de enteros, por lo que el resultado a o es siempre 0, incluso 59 minutos despu´s de la hora. e Una alternativa es convetir minuto a tipo float (coma flotante) y luego efectuar una divisi´n de coma flotante: o
  • 49. 3.4 Funciones matem´ticas a 23 >>> minuto = 59 >>> float(minuto) / 60.0 0.983333333333 O bien podemos sacar provecho de las reglas de la conversi´n autom´tica de o a tipos, llamada coerci´n de tipos. Para los operadores matem´ticos, si uno de o a los operandos matem´ticos es tipo float, el otro se convierte autom´ticamente a a en float. >>> minuto = 59 >>> minuto / 60.0 0.983333333333 Al usar un denomidador que es float, obligamos a Python a hacer divisi´n de o coma flotante. 3.4. Funciones matem´ticas a Es posible que ya haya visto usted en matem´ticas funciones como sin (seno) y a log, 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 e es 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 ı a a 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- a plicadas 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 ıa funciones matem´ticas habituales. Un m´dulo es un archivo que contiene una a o colecci´n de funciones agrupadas juntas. o Antes de poder usar las funciones de un m´dulo, tenemos que importarlo: o >>>import math Para llamar a una de las funciones, tenemos que especificar el nombre del m´dulo o y el nombre de la funci´n, separados por un punto. A esto se le llama notaci´n o o de punto: decibelio = math.log10 (17.0) angulo = 1.5 altura = math.sin(angulo)
  • 50. 24 Funciones La 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 o La tercera sentencia halla el seno del valor de la variable angulo. sin y las otras funciones trigonom´tricas (cos, tan, etc.) toman sus argumentos en radianes. e Para convertir de grados a radianes, puede dividir por 360 y multiplicar por 2*pi. Por ejemplo, para hallar el seno de 45 grados, calcule primero el ´ngulo a en 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 ız 2. >>> math.sqrt(2) / 2.0 0.707106781187 3.5. Composici´n o Igual que con las funciones matem´ticas, las funciones de Python se pueden a componer; eso quiere decir que se usa una expresi´n como parte de otra. Por o ejemplo, 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 n al valor de angulo. La suma se pasa luego como argumento a la funci´n cos. o Tambi´n puede tomar el resultado de una funci´n y pas´rselo como argumento e o a a otra: x = math.exp(math.log(10.0)) Esta sentencia encuentra el logaritmo en base e de 10 y luego eleva e a ese exponente. El resultado queda asignado a x. 3.6. A˜ adir funciones nuevas n Hasta ahora s´lo hemos usado las funciones que vienen incluidas con Python, o pero tambi´n es posible a˜adir nuevas funciones. La creaci´n de nuevas funciones e n o para 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
  • 51. 3.6 A˜ adir funciones nuevas n 25 En contextos de programaci´n, funci´n es una secuencia de instrucciones con o o nombre, que lleva a cabo la operaci´n deseada. Esta operaci´n se especifica en o o una definici´n de funci´n. Las funciones que hemos usado hsta ahora las han o o definido por nosotros, y esas definiciones est´n ocultas. Eso es bueno, ya que nos a permite 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 ): SENTENCIAS Puede inventarse el nombre que desee para su funci´n, con la excepci´n de o o que no puede usar las palabras reservadas de Python. La lista de par´metros a especifica qu´ informaci´n, en caso de haberla, ha de proporcionar para usar la e o funci´n nueva. o Puede haber cualquier n´mero de sentencias dentro de la funci´n, pero tienen u o que estar indentadas desde el margen izquierdo. En los ejemplos de este libro se usar´ una indentaci´n de dos espacios. a o El primer par de funciones que escribiremos no tienen par´metros, de manera a que su sintaxis es: def nueva_linea(): print Esta funci´n se llama nueva linea. Los par´ntesis vac´ indican que no tiene o e ıos par´metros. Contiene una unica sentencia, que muestra como salida un car´cter a ´ a de 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 o para 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 a espacios, entre las l´ ıneas, ¿qu´ har´ e ıamos? Podemos llamar varias veces a la misma funci´n: o print "Primera linea." nueva_linea() nueva_linea() nueva_linea() print "Segunda linea."
  • 52. 26 Funciones O bien podemos escribir una nueva funci´n que llamaremos tresLineas, y que o imprima 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 a espacios. Puesto que la siguiente sentencia no est´ indentada, Python sabe que a no es parte de la funci´n. o Observe 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 e estas funciones nuevas. En realidad hay much´ ısimas razones, pero este ejemplo demuestra 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 uso Juntando los fragmentos de c´digo de la secci´n anterior, el programa completo o o queda de la siguiente manera:
  • 53. 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 y tresLineas. 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 o la funci´n no se ejecutan hasta que se llama a la funci´n, y la definici´n de la o o o funci´n no genera salida. o Como era de esperar, tiene que crear una funci´n antes de poder ejecutarla. o En otras palabras, la definici´n de la funci´n tiene que ejecutarse antes de la o o primera 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 o Para asegurarse de que una funci´n se define antes de su primer uso, tiene que o conocer el orden en el que se ejecutan las sentencias; a esto se le llama flujo de ejecuci´n. o La ejecuci´n comienza siempre por la primera sentencia del programa. Las sen- o tencias se ejecutan a raz´n de una cada vez, en orden, hasta que se alcanza una o llamada a una funci´n. o Las definiciones de funciones no alteran el flujo de ejecuci´n del programa, pero o recuerde que las sentencias que hay dentro de la funci´n no se ejecutan hasta o que se hace la llamada a la funci´n. Aunque no es habitual, puede definir una o
  • 54. 28 Funciones funci´n dentro de otra. En este caso, la definici´n de funci´n interior no se o o o ejecuta hasta que no se llama a la funci´n exterior. o Las llamadas a funciones son como un desv´ en el flujo de ejecuci´n. En lugar ıo o de ir a la siguiente sentencia, el flujo salta hasta la primera l´ ınea de la funci´n o a 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 o Esto suena bastante sencillo... hasta que se acuerda de que una funci´n puede o llamar a otra. Mientras estamos en medio de una funci´n, podr´ o ıamos vernos obligados a abandonarla e ir a ejecutar sentencias en otra funci´n m´s. Pero o a mientras estamos en esta nueva funci´n, ¡podr´ o ıamos salirnos y ejecutar otra funci´n m´s! o a Afortunadamente, a Python se le da bien tomar nota de d´nde est´, de manera o a que cada vez que se completa una funci´n, el programa retoma el punto en donde o lo dej´ en la funci´n que hizo la llamada. Cuando llega al final del programa, o o termina. ¿Cu´l es la moraleja de toda esta historia? Cuando est´ leyendo un programa, a e no lo lea desde la parte superior a la inferior. En lugar de eso, siga el flujo de ejecuci´n. o 3.9. Par´metros y argumentos a Algunas de las funciones internas que hemos usado precisan de argumentos, los valores que controlan c´mo la funci´n lleva a cabo su tarea. Por ejemplo, si o o desea encontrar el seno de un n´mero, tiene que indicar de qu´ n´mero se trata. u e u As´ pues, sin toma como argumento un valor num´rico. ı e Algunas funciones toman m´s de un argumento, como pow, que toma dos argu- a mentos: la base y el exponente. Dentro de la funci´n, los valores que se le han o pasado se asignan a variables llamadas par´metros. a He aqu´ un ejemplo de una funci´n definida por el usuario, que toma un par´me- ı o a tro: def imprimeDoble(paso): print paso, paso Esta funci´n toma un unico argumento y se lo asigna a un par´metro llamado o ´ a paso. El valor del par´metro (en este punto todav´ no tenemos ni idea de cu´l a ıa a ser´) se imprime dos veces, seguido por un car´cter de nueva l´ a a ınea. El nombre paso se eligi´ para sugerir que el nombre que le d´ a un par´metro depende de o e a usted, pero en general es mejor que elija un nombre m´s ilustrativo que paso. a
  • 55. 3.10 Las variables y los par´metros son locales a 29 La funci´n imprimeDoble sirve con cualquier tipo (de dato) que se pueda im- o primir: >>> imprimeDoble(’Jam´n’) o Jam´n Jam´n o o >>> imprimeDoble(5) 5 5 >>> imprimeDoble(3.14159) 3.14159 3.14159 En la primera llamada a la funci´n, el argumento es una cadena; en la segunda o es un entero, y en la tercera es un n´mero de coma flotante. u Las mismas reglas de composici´n que se aplican a las funciones internas se o aplican 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.0 Como de costumbre, se eval´a la expresi´n antes de ejecutar la funci´n, de modo u o o que imprimeDoble devuelve Jam´nJam´nJam´nJam´n Jam´nJam´nJam´nJam´n o o o o o o o o en lugar de ’Jam´n’*4’Jam´n’*4. o o Asimismo 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 variable que pasamos como argumento (latoya) no tiene nada que ver con el nombre del par´metro (paso). No importa c´mo se llamaba el valor en su lugar original (el a o lugar desde donde se invoc´); aqu´ en imprimeDoble llamamos a todo el mundo o ı paso. 3.10. Las variables y los par´metros son locales a Cuando crea una variable dentro de una funci´n, s´lo existe dentro de dicha o o funci´n, y no puede usarla fuera de ella. Por ejemplo, la funci´n o o >>> def catDoble(parte1, parte2): ... cat = parte1 + parte2 ... imprimeDoble(cat) ... >>>
  • 56. 30 Funciones toma 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- a mirla, obtendr´ ıamos un error: >>> print cat NameError: cat Los par´metros tambi´n son locales. Por ejemplo, una vez fuera de la funci´n a e o imprimeDoble, no existe nada llamado paso. Si trata de usarla, Python se que- jar´. a 3.11. Diagramas de pila Para 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 diagramas de pila muestran el valor de cada variable, pero tambi´n muestran la funci´n a e o la 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. Por e a o ejemplo, 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 o por catDoble y a catDoble lo invoc´ main , que es un nombre especial de la o
  • 57. 3.12 Funciones con resultado 31 funci´n m´s alta. Cuando crea una variable fuera de cualquier funci´n, pertenece o a o a main . En cada caso, el par´metro se refiere al mismo valor que el argumento corres- a pondiente. 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 o de la funci´n y el nombre de la funci´n que la llam´, y el nombre de la funci´n o o o o que llam´ a ´sa, y as´ hasta main . o e ı Por ejemplo, si intentamos acceder a cat desde imprimeDoble, provocaremos un 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 cat NameError: cat Esta lista de funciones de llama traceback (traza inversa). Le dice a usted en qu´ archivo de programa sucedi´ el error, y en qu´ l´ e o e ınea, y qu´ funciones se e ejecutaban en ese momento. Tambi´n muestra la l´ e ınea de c´digo que caus´ el o o error. ıjese en la similaridad entre la traza inversa y el diagrama de pila. No es una F´ coincidencia. 3.12. Funciones con resultado Seguramente ha notado ya que algunas de las funciones que estamos usando, igual que las funciones matem´ticas, devuelven un resultado. Otras funciones, a como nueva linea, llevan a cabo una acci´n pero no devuelven un valor. Ello o suscita 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?
  • 58. 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. Glosario llamada 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. e argumento: 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 o valor 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 o conversi´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. o m´dulo: Fichero que contiene una colecci´n de funciones y clases relacionadas. o o notaci´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. o funci´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. a flujo de ejecuci´n: Orden en el que se ejecutan las sentencias durante la eje- o cuci´n de un programa. o
  • 59. 3.13 Glosario 33 par´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 o diagrama 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
  • 61. Cap´ ıtulo 4 Condicionales y recursividad 4.1. El operador m´dulo o El operador m´dulo funciona con enteros (y expresiones enteras), y devuelve o el resto de dividir el primer operando entre el segundo. En Python, el operador de m´dulo es el signo de tanto por ciento ( %). La sintaxis es la misma de los o otros operadores: >>> cociente = 7 / 3 >>> print cociente 2 >>> resto = 7 % 3 >>> print resto 1 As´ 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 u divisible entre y. Tambi´n puede usar el operador m´dulo para extraer el d´ e o ıgito m´s a la derecha a de un n´mero. Por ejemplo, x % 10 devuelve el d´ u ıgito m´s a la derecha de x (en a base 10). De forma similar, x % 100 devuelve los dos ultimos d´ ´ ıgitos.
  • 62. 36 Condicionales y recursividad 4.2. Expresiones booleanas Una expresi´n booleana es una expresi´n que es cierta o falsa. En Python, o o una expresi´n que es cierta tiene el valor 1, y una expresi´n que es falsa tiene o o el valor 0. El operador == compara dos valores y entrega una expresi´n booleana: o >>> 5 == 5 1 >>> 5 == 6 0 En la primera sentencia, los dos operandos son iguales, as´ que la expresi´n se ı o eval´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 y Aunque probablemente estas operaciones le resulten familiares, los s´ımbolos en Python son diferentes de los matem´ticos. Un error habitual es utilizar un signo a igual sencillo (=) en lugar del doble (==). Recuerde que = es un operador de asignaci´n y == es un operador de comparaci´n. Adem´s, no existen =< ni =>. o o a 4.3. Operadores l´gicos o Hay tres operadores l´gicos: and, or, y not. La sem´ntica (significado) de o a estos operadores es similar a sus significados en ingl´s. Por ejemplo, x >0 and e x <10 es verdadero s´lo si x es mayor que 0 y menor que 10. o n %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. u Finalmente, 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 ıan expresiones booleanas, pero Python no es muy estricto. Cualqueir n´mero que u no sea cero se interpreta como “verdadero”.
  • 63. 4.4 Ejecuci´n condicional o 37 >>> x = 5 >>> x and 1 1 >>> y = 0 >>> y and 1 0 En general, este tipo de cosas no se considera buen estilo. Si quiere comparar un valor con cero, deber´ hacerlo expl´ ıa ıcitamente. 4.4. Ejecuci´n condicional o Para 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 a sencilla 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 o la sentencia indentada se ejecuta. Si la condici´n no es verdadera, no pasa nada. o Como otras sentencias compuestas, if consta de una cabecera y un bloque de sentencias: CABECERA: PRIMERA SENTENCIA ... ULITMA SENTENCIA La 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. La primera sentencia no indentada marca el fin del bloque. Un bloque de sentencias dentro 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 de una 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 o todav´ no ha escrito). En tales casos, puede usted utilizar la sentencia pass, ıa que no hace nada. 4.5. Ejecuci´n alternativa o Una segunda forma de la sentencia if es la ejecuci´n alternativa, en la que hay o dos posibilidades, y la condici´n determina cu´l de ellas se ejecuta. La sintaxis o a
  • 64. 38 Condicionales y recursividad tiene 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 o ejecuta el segundo lote de sentencias. Puesto que la condici´n debe ser verdadera o o falsa, se ejecutar´ exactamente una de las alternativas. Llamamos ramas a a las posibilidades porque son ramas del flujo de ejecuci´n. o Como un aparte, si piensa que querr´ comprobar con frecuencia la paridad de a n´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- o piado para cada n´mero entero que usted le pase. Llame a esta funci´n de la u o manera siguiente: >>> imprimeParidad(17) >>> imprimeParidad(y+1) 4.6. Condiciones encadenadas A veces hay m´s de dos posibilidades y necesitamos m´s de dos ramas. Una a a forma 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 a hay l´ ımite al n´mero de sentencias elif, pero s´lo se permite una sentencia u o else (que puede omitirse) y debe ser la ultima rama de la sentencia: ´ if eleccion == ’A’: funcionA() elif eleccion == ’B’:
  • 65. 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 comprueba la 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 o la primera rama verdadera. Como ejercicio, envuelva estos ejemplos en funciones llamadas compara(x, y) y resuelve(eleccion). 4.7. Condiciones anidadas Una 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", y La condici´n externa que contiene dos ramas. La primera rama contiene una o sentencia simple de salida. La segunda rama contiene otra sentencia if, que tiene dos ramas en s´ misma. Estas dos ramas son ambas sentencias de salida ı de datos, aunque podr´ ser igualmente sentencias condicionales. ıan Aunque la indentaci´n de las sentencias hace la estructura evidente, las condi- o ciones anidadas en seguida se vuelven dif´ ıciles de leer. En general es una buena idea evitarlas cuando pueda. Los operadores l´gicos suelen facilitar un modo de simplificar las sentencias o condicionales anidadas. Por ejemplo, podemos reescribir el c´digo siguiente con o un s´lo condicional: o if 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, o as´ que podemos usar el operador and: ı
  • 66. 40 Condicionales y recursividad if 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 proporciona una sintaxis alternativa similar a la notaci´n matem´tica: o a if 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 o ta y que el condicional anidado. 4.8. La sentencia return La sentencia return le permite terminar la ejecuci´n de una funci´n antes de o o alcanzar 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", result La funci´n imprimeLogaritmo toma un par´metro llamado x. Lo primero que o a hace es comprobar si x es menor o igual que cero, en cuyo caso muestra un mensaje de error y luego usa return para salir de la funci´n. El flujo de la o ejecuci´n vuelve inmediatamente al llamante y no se ejecutan las l´ o ıneas restantes de la funci´n. o Recuerde que para usar una funci´n del m´dulo math tiene que importarlo. o o 4.9. Recursividad Ya mencionamos que es legal que una funci´n llame a otra, y de ello hemos visto o ya varios ejemplos. Olvidamos mencionar que tambi´n es legal que una funci´n e o se llame a s´ misma. Puede no resultar evidente por qu´ es bueno esto, pero ı e viene a resultar una de las cosas m´s interesantes y curiosas que puede hacer a un programa. Examine por ejemplo la siguiente funci´n: o
  • 67. 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 a tro es cero, muestra la palabra “Despegando!”. En otro caso, muestra n y luego llama a la funci´n llamada cuenta atras (ella misma) pas´ndole como argu- o a mento 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 o como 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 a salida completa presenta el siguiente aspecto: 3 2 1 Despegando! Como segundo ejemplo, consideremos de nuevo las funciones nuevaLinea and tresLineas. def nuevaLinea(): print
  • 68. 42 Condicionales y recursividad def tresLineas(): nuevaLinea() nuevaLinea() nuevaLinea() Aunque todas funcionan, no ser´ de mucha ayuda si quisiera mostrar 2 l´ ıan ıneas nuevas 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), que si rescata su ´lgebra ver´ que es n. a a El 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- vas El la Secci´n 3.11 utilizamos un diagrama de pila para representar el estado de o un programa durante la llamada de una funci´n. El mismo tipo de diagrama o puede hacer m´s f´cil interpretar una funci´n recursiva. a a o Cada vez que se llama a una funci´n, Python crea un nuevo marco para la o funci´n, que contiene sus variables locales y par´metros. En el caso de una o a funci´n recursiva, puede haber m´s de un marco en la pila al mismo tiempo. o a La figura muestra un diagrama de pila para cuenta atras, invocada con n = 3:
  • 69. 4.11 Recursividad infinita 43 __main__ countdown n 3 countdown n 2 countdown n 1 countdown n 0 Como es habitual, en lo alto de la pila est´ el marco de main . Est´ vac´ a a ıa porque no hemos ninguna variable sobre main ni le hemos pasado ning´n u par´metro. a Los cuatro marcos de cuenta atras tienen valores diferentes para el par´metro a n. El fondo de la pila, donde n=0, se llama caso base. No hace una llamada recursiva, 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. a 4.11. Recursividad infinita Si una recursi´n no alcanza nunca el caso base, seguir´ haciendo llamadas re- o a cursivas para siempre y nunca terminar´. Esta circunstancia se conoce como a recursividad infinita, y generalmente no se la considera una buena idea. Este es un programa m´ ınimo con recursividad infinita: def recurre(): recurre() El la mayor´ de los entornos de programaci´n, un programa con recursividad ıa o infinita no se ejecutar´ realmente para siempre. Python informar´ de un mensaje a a de 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 recurse RuntimeError: Maximum recursion depth exceeded
  • 70. 44 Condicionales y recursividad Esta 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 e 4.12. Entrada por teclado Los programas que hemos escrito hasta ahora son un poco maleducados en el sentido de que no aceptan entradas de datos del usuario. Simplemente hacen lo mismo 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 o grama se detiene y espera a que el usuario escriba algo. Cuando el usuario pulsa la tecla Return o Enter, el programa se reanuda y raw input devuelve lo que el usuario escribi´ como tipo string: o >>> entrada = raw_input () A qu´ est´s esperando? e a >>> print entrada A qu´ est´s esperando? e a Antes de llamar a raw input es conveniente mostrar un mensaje que le pida al usuario el dato solicitado. Este mensaje se llama indicador (prompt en ingl´s). e Puede 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 e Si 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 a asignar´ a velocidad. Por desgracia, si el usuario escribe algo que no sea un a d´ı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 syntax Para evitar este tipo de error, generalmente es buena idea usar raw input para obtener una cadena y usar entonces las funciones de conversi´n para convertir o a otros tipos.
  • 71. 4.13 Glosario 45 4.13. Glosario operador 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 o operador 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. o condici´n: La expresi´n booleana de una sentencia condicional que determina o o qu´ rama se ejecutar´. e a sentencia 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. ıa bloque: 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. o indicador: indicador visual que invita al usuario a introducir datos.
  • 73. Cap´ ıtulo 5 Funciones productivas 5.1. Valores de retorno Algunas de las funciones internas que hemos usado, como las funciones math o funciones matem´ticas, han producido resultados. Llamar a la funci´n genera un a o nuevo valor, que normalmente asignamos a una variable pasa usar como parte de 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 un valor. En este cap´ ıtulo escribiremos funciones que devuelvan valores, que llamaremos funciones productivas, a falta de un nombre mejor. El primer ejemplo es area, que devuelve el ´rea de un c´ a ırculo con un radio dado: import math def area(radio): temporal = math.pi * radio**2 return temporal Ya hemos visto antes la sentencia return, pero en una funci´n productiva la o sentencia 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 o retorno”. La expresi´n dada puede ser arbitrariamente complicada; as´ pues, o ı podr´ıamos haber escrito esta funci´n m´s concisamente: o a
  • 74. 48 Funciones productivas def area(radio): return math.pi * radio**2 Por otra parte, las variables temporales como temporal suelen hacer m´s a f´cil el depurado. a A 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 x Puesto que estas sentencias return est´n en una condici´n alternativa, s´lo se a o o ejecutar´ una de ellas. En cuanto se ejecuta una de ellas, la funci´n termina sin a o ejecutar ninguna de las sentencias siguientes. El c´digo que aparece despu´s de una sentencia return o en cualquier otro lugar o e donde el flujo de ejecuci´n no pueda llegar, recibe el nombre de c´digo muerto. o o En una funci´n productiva es una buena idea asegurarse de que cualquier posible o recorrido del programa alcanza una sentencia return. Por ejemplo: def valorAbsoluto(x): if x < 0: return -x elif x > 0: return x Este programa no es correcto porque si resulta que x vale 0, entonces no se cum- ple ninguna de ambas condiciones y la funci´n termina sin alcanzar la setencia o return. 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 programas Llegados a este punto, tendr´ que poder mirar a funciones Python completas ıa y adivinar qu´ hacen. Tambi´n, si ha hecho los ejercicios, habr´ escrito algu- e e a nas funcioncillas. Tal como vaya escribiendo funciones mayores puede empezar
  • 75. 5.2 Desarrollo de programas 49 a experimentar m´s dificultades, especialmente con los errores en tiempo de a ejecuci´n y los sem´nticos. o a Para lidiar con programas de complejidad creciente, vamos a sugerirle una t´cni- e ca 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 o peque˜as porciones de c´digo en cada vez. n o Por 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 a escribir 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 o Python. En otras palabras, ¿cu´les son las entradas (par´metros) y cu´l es la a a a salida (valor de retorno)? En este caso, los dos puntos son los par´metros, que podemos representar usando a cuatro par´metros. El valor de retorno es la distancia, que es un valor en coma a flotante. Ya podemos escribir un bosquejo de la funci´n: o def distancia(x1, y1, x2, y2): return 0.0 Obviamente, la funci´n no calcula distancias; siempre devuelve cero. Pero es o sint´cticamente correcta y se ejecutar´, lo que significa que podemos probarla a a antes de complicarla m´s. a Para 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 la distancia vertical sea igual a 4; de esa manera el resultado es 5 (la hipotenusa del tri´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 a ta, as´ que podemos empezar a a˜adir l´ ı n ıneas de c´digo. Despu´s de cada cambio o e
  • 76. 50 Funciones productivas incremental, comprobamos de nuevo la funci´n. Si en un momento dado apare- o ce un error, sabremos d´nde est´ exactamente: en la ultima l´ o a ´ ınea que hayamos a˜adido. n El siguiente paso en el c´lculo es encontrar las diferencias entre x2 −x1 y y2 −y1 . a Almacenaremos dichos valores en variables temporales llamadas dx y dy y las imprimiremos. def distancia(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 print "dx es", dx print "dy es", dy return 0.0 Si la funci´n funciona, valga la redundancia, las salidas deber´ ser 3 y 4. Si o ıan es as´ sabemos que la funci´n recibe correctamente los par´metros y realiza ı, o a correctamente 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.0 F´ı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). ıa Por ultimo, si hemos importado el m´dulo math, podemos usar la funci´n sqrt ´ o o para 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 resultado Si esto funciona correctamente, ha terminado. Si no, podr´ ser que quisiera ıa usted imprimir el valor de resultado antes de la sentencia return.
  • 77. 5.3 Composici´n o 51 Al principio, deber´ a˜adir solamente una o dos l´ ıa n ıneas de c´digo cada vez. o Conforme vaya ganando experiencia, puede que se encuentre escribiendo y de- purando trozos mayores. Sin embargo, el proceso de desarrollo incremental puede ahorrarle 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 o Como seguramente a estas alturas ya supondr´, se puede llamar a una funci´n a o desde dentro de otra. Esta habilidad se llama composici´n . o Como ejemplo, escribiremos una funci´n que tome dos puntos, el centro del o c´ ı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 a que el punto del per´ ımetro lo est´ en xp e yp. El primer paso es hallar el radio a del c´ ırculo, que es la distancia entre los dos puntos. Afortunadamente hay una funci´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 resultado Envolviendo 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
  • 78. 52 Funciones productivas Hemos llamado a esta funci´n area2 para distinguirla de la funci´n area definida o o anteriormente. S´lo puede haber una unica funci´n con un determinado nombre o ´ o dentro de un m´dulo. o Las 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 a conciso 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´ e 5.4. Funciones booleanas Las funciones pueden devolver valores booleanos, lo que a menudo es conveniente para 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 false La funci´n lleva por nombre esDivisible. Es habitual dar a las funciones boo- o leanas nombres que suenan como preguntas s´ı/no. Devuelve 1 ´ 0 para indicar o si 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 a la sentencia condicional que hay despu´s del if es en s´ misma una expresi´n e ı o booleana. Podemos devolverla directamente, evitando a la vez la sentencia if: def esDivisible(x, y): return x % y == 0 La siguiente sesi´n muestra a la nueva funci´n en acci´n: o o o >>> esDivisible(6, 4) 0 >>> esDivisible(6, 3) 1 El uso m´s com´n para las funciones booleanas es dentro de sentencias condi- a u cionales:
  • 79. 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 a Hasta ahora, usted ha aprendido solamente un peque˜o subconjunto de Python, n pero puede que le interese saber que ese subconjunto es ya un lenguaje de programaci´n completo; con esto queremos decir que cualquier cosa que pueda o computarse se puede expresar en este lenguaje. Cualquier programa que se haya escrito alguna vez puede reescribirse utilizando unicamente las caracter´ ´ ısticas del 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, pero o o eso es todo). Probar tal afirmaci´n es un ejercicio nada trivial, completado por primera vez o por Alan Turing, uno de los primeros cient´ ıficos inform´ticos (algunos argumen- a tar´n que era un matem´tico, pero muchos de los cient´ a a ıficos inform´ticos pione- a ros comenzaron como matem´ticos). En correspondencia, se la conoce como la a tesis de Turing. Si estudia un curso de Teor´ de la Computaci´n, tendr´ opor- ıa o a tunidad de ver la prueba. Para darle una idea de lo que puede hacer con las herramientas que ha aprendido hasta ahora, evaluaremos una serie de funciones matem´ticas que se definen a recursivamente. Una definici´n recursiva es semejante a una definici´n circular, o o en el sentido de que la definici´n contiene una referencia a lo que se define. Una o definici´n verdaderamente circular no es muy util: o ´ frangoso: adjetivo que describe algo que es frangoso Si usted viera esa definici´n en el diccionario, se quedar´ confuso. Por otra parte, o ıa si ha buscado la definici´n de la funci´n matem´tica factorial, habr´ visto algo o o a a sejemante a lo siguiente:
  • 80. 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 o otro 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 a un programa de Python para evaluarlo. El primer paso es decidir cu´les son los a par´metros para esta funci´n. Con poco esfuerzo llegar´ a la conclusi´n de que a o a o factorial 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 1 En 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 resultado El flujo de ejecuci´n de este programa es similar al de cuenta atras de la o Secci´n 4.9. Si llamamos a factorial con el valor 3: o Puesto que 3 no es 0, tomamos la segunda rama y calculamos el factorial de n-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.
  • 81. 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 convierte en el valor de retorno de la llamada a la funci´n que comenz´ todo el proceso. o o He 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 0 Los valores de retorno se muestran seg´n se pasan hacia la parte superior de u la pila. En cada marco, el valor de retorno es el valor de resultado, que es el producto 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 fe Seguir el flujo de ejecuci´n es una de las maneras de leer programas; pero puede o volverse r´pidamente una tarea laber´ a ınitca. La alternativa es lo que llamamos el “acto de fe”. Cuando llegamos a una funci´n, en lugar de seguir el flujo de o ejecuci´n, damos por sentado que la funci´n trabaja correctamente y devuelve o o el 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
  • 82. 56 Funciones productivas dichas funciones. Simplemente da por sentado que funcionan porque los que escribieron 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 o que determina si un n´mero es divisible por otro. Una vez que nos hayamos u convencido de que dicha funci´n es correcta, comprobando y examinando el o c´digo, podremos usar la funci´n sin tener siquiera que volver a mirar el c´digo o o o otra 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 ıa la llamada recursiva funciona (es decir, devuelve el resultado correcto) y luego preguntarse: “suponiendo que puedo hallar el factorial de n − 1, ¿puedo hallar el factorial de n?” En este caso, est´ claro que s´ puede, multiplic´ndolo por n. a ı a Por supuesto, es un tanto extra˜o dar por supuesto que la funci´n est´ bien n o a cuando ni siquiera ha acabado de escribirla, pero precisamente por eso se llama acto de fe. 5.7. Un ejemplo m´s a En 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 ıamos habernos 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 a mendamos que utilice la versi´n m´s expl´ o a ıcita mientras se halle desarrollando c´digo. Cuando lo tenga funcionando, lo podr´ acortar, si se siente inspirado. o a Despu´s de factorial, el ejemplo m´s com´n de una funci´n matem´tica re- e a u o a cursivamente 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:
  • 83. 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 n fe, si da por supuesto que las dos llamadas recursivas funcionan correctamente, entonces estar´ claro que obtiene el resultado correcto al sumarlas juntas. a 5.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 exceeded Tiene todo el aspecto de una recursi´n infinita. Pero, ¿c´mo ha podido ocurrir? o o Hay una condici´n de salida o caso base: cuando n == 0. El problema es que el o valor de n yerra el caso base. En la primera llamada recursiva, el valor de n es 0.5. En la siguiente vez su valor es -0.5. A partir de ah´ se vuelve m´s y m´s peque˜o, pero nunca ser´ 0. ı, a a n a Tenemos dos opciones. Podemos intentar generalizar la funci´n factorial para o que trabaje con n´meros de coma flotante, o podemos hacer que factorial u compruebe el tipo de su par´metro. La primera opci´n se llama funci´n gamma, a o o y 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 a de un valor entero conocido (por ejemplo 1). Ya que estamos en ello, podemos asegurarnos 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)
  • 84. 58 Funciones productivas Ahora tenemos tres condiciones de salida o casos base. El primero filtra los n´meros no enteros. El segundo evita los enteros negativos. En ambos casos, se u muestra un mensaje de error y se devuelve un valor especial, -1, para indicar a quien 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. -1 Si pasamos ambas comprobaciones, entonces sabemos que n es un entero positivo y podemos probar que la recursi´n termina. o Este programa muestra un patr´n que se llama a veces guardi´n. Las primeras o a dos condicionales act´an como guardianes, protegiendo al c´digo que sigue de u o los valores que pudieran causar errores. Los guardianes hacen posible demostrar la correcci´n del c´digo. o o 5.9. Glosario funci´n productiva: Funci´n que devuelve un valor de retorno. o o valor de retorno: El valor obtenido como resultado de una llamada a una funci´n. o variable temporal: Variable utilizada para almacenar un valor intermedio en un c´lculo complejo. a c´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
  • 85. 5.9 Glosario 59 guardi´n: Una condici´n que comprueba y maneja circunstancias que pudieran a o provocar un error.
  • 87. Cap´ ıtulo 6 Iteraci´n o 6.1. Asignaci´n m´ ltiple o u Es posible que haya descubierto que es posible hacer m´s de una asignaci´n a a o una misma variable. El efecto de la nueva asignaci´n es redirigir la variable de o manera que deje de remitir al valor antiguo y empieze a remitir al valor nuevo. bruno = 5 print bruno, bruno = 7 print bruno La salida del programa es 5 7, ya que la primera vez que imprimimos bruno su valor es 5, y la segunda vez su valor es 7. La coma al final de la primera sentencia print impide que se imprima una nueva l´ ınea en ese punto, por eso ambos 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 7 Cuando hay asignaciones m´ltiples a una variable, es especialmente importante u distinguir entre una sentencia de asignaci´n y una sentencia de igualdad. Puesto o que Python usa el s´ ımbolo = para la asignaci´n, es tentador interpretar una o sentencia como a = b como sentencia de igualdad. Pero no lo es.
  • 88. 62 Iteraci´n o Para empezar, la igualdad es commutativa, y la asignaci´n no lo es. Por ejemplo o en matem´ticas si a = 7 entonces 7 = a. Pero en Python la sentencia a = 7 es a legal, y 7 = a no lo es. Y lo que es m´s, en matem´ticas, una sentencia de igualdad es verdadera todo a a el tiempo. Si a = b ahora, entonces a siempre ser´ igual a b. En Python, una a sentencia de asignaci´n puede hacer que dos variables sean iguales, pero no o tienen por qu´ quedarse as´ e ı. a = 5 b = a # a y b son ahora iguales a = 3 # a y b ya no son iguales La tercera l´ ınea cambia el valor de a pero no cambia el valor de b, y por lo tanto ya dejan de ser iguales. En algunos lenguajes de programaci´n, se usa para la o asignaci´n un s´ o ımbolo distinto, como <- o como :=, para evitar la confusi´n.o Aunque 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 del programa, podr´ suceder que el c´digo sea dif´ de leer y mantener. ıa o ıcil 6.2. La sentencia while Una de las tareas para las que los computadores se usan con frecuencia es la automatizaci´n de tareas repetitivas. Repetir tareas similares o id´nticas es algo o e que los computadores hacen bien y las personas no hacen tan bien. Hemos visto dos programas, nLineas y cuenta atras, que usan la recursividad para llevar a cabo la repetici´n, que tambi´n se llama iteraci´n. Por ser la o e o iteraci´n tan habitual, Python proporciona como lenguaje varias caracter´ o ısticas que la hacen m´s f´cil. La primera caracter´ a a ıstica que vamos a considerar es la sentencia 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. o Casi podr´ leer una sentencia while como si fuera ingl´s (castellano “mien- ıa e tras”). Quiere decir que “Mientras n sea mayor que cero, contin´a mostrando el u valor de n y despu´s rest´ndole 1 al valor de n. Cuando llegues a cero, muestra e a la palabra “Despegando!”.
  • 89. 6.2 La sentencia while 63 M´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 a el 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 o sentencias 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, a llegado el momento, la condici´n sea falsa y el bucle termine. En caso contrario, o el bucle se repetir´ para siempre, que es lo que se llama bucle infinito. Una a infinita fuente de diversi´n para los cient´ o ıficos inform´ticos es la observaci´n de a o que las instrucciones del champ´ “lavar, aclarar, repetir”, son un bucle infinito. u En el caso de cuenta atras, podemos probar que el bucle terminar´ porque a sabemos que el valor de n es finito, y podemos ver que el valor de n disminuye cada vez que se atraviesa el bucle (cada iteraci´n), de manera que ea la larga o tenemos 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+1 La condici´n de este bucle es n != 1, de manera que el bucle continuar´ hasta o a que n sea 1, que har´ que la condici´n sea falsa. a o En cada iteraci´n, el programa muestra como salida el valor de n y luego com- o prueba 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 argumento pasado 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 de que n alcance alguna vez el valor 1, o de que el programa vaya a terminar. Para algunos valores particulares de n, podemos probar la terminaci´n. Por ejemplo, o
  • 90. 64 Iteraci´n o si el valor de inicio es una potencia de dos, entonces el valor de n ser´ par cada a vez que se pasa a trav´s del bucle, hasta que lleguemos a 1. El ejemplo anterior e acaba 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, a nadie 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. o 6.3. Tablas Una 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 ıa matem´ticas. Para facilitarlo, los libros de matem´ticas conten´ largas tablas a a ına donde aparec´ los valores de estas funciones. Confeccionar estas tablas era ıan una tarea lenta y pesada, y el resultado estaba lleno de erratas. Cuando los computadores aparecieron en escena, una de las primeras reacciones fue “¡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 e computadores y las calculadoras cient´ıficas se hicieron tan ubicuas que las tablas resultaron obsoletas. Bueno, casi. Resulta que para ciertas operaciones, los computadores usan tablas para obtener un valor aproximado, y luego ejecutan c´lculos para mejorar la a aproximaci´n. En algunos casos, ha habido errores en las tablas subyacentes; el o m´s famoso estaba en la tabla que el Pentium de Intel usaba para llevar a cabo a la divisi´n de coma flotante. o Aunque una tabla logar´ıtmica ya no es tan util como lo fuera anta˜o, todav´ ´ n ıa constituye un buen ejemplo de iteraci´n. El siguiente programa muestra una o secuencia de valores en la columna izquierda y sus logaritmos en la columna derecha: x = 1.0 while x < 10.0: print x, ’t’, math.log(x) x = x + 1.0 El t representa un car´cter de tabulaci´n. a o
  • 91. 6.3 Tablas 65 Tal como se van mostrando en la pantalla caracteres y cadenas, un se˜alador n invisible llamado cursor lleva la cuenta de d´nde ir´ el pr´ximo car´cter. Tras o a o a una 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 o alcance 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.19722457734 Si estos valores le parecen raros, recuerde que la funci´n log usa como base e. o Debido a que las potencias de dos son muy importantes en las ciencias de la computaci´n, generalmente querremos hallar los logaritmos en relaci´n con la o o base dos. Para llevarlo a cabo, podemos usar la siguiente f´rmula: o loge x log2 x = (6.1) loge 2 Cambiar 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.16992500144 Podemos ver que 1, 2, 4 y 8 son potencias de dos, porque sus logaritomos de base 2 son n´meros enteros. Si quisi´ramos encontrar los logaritmos de otras u e potencias de dos, podr´ ıamos modificar el programa de la siguiente manera: x = 1.0 while x < 100.0:
  • 92. 66 Iteraci´n o print x, ’t’, math.log(x)/math.log(2.0) x = x * 2.0 Ahora, en lugar de a˜adir algo a x cada vez que atravesamos el bucle, que n devuelve una secuencia aritm´tica, multiplicamos x por algo, devolviendo una e secuencia 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.0 Debido a que usamos caracteres de tabulaci´n entre las columnas, la posici´n de o o la 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 a escape. Las secuencias de escape se usan para representar caracteres invisibles como tabulaciones y retornos de carro. La secuencia n representa un retorno de carro. Una sentencia de escape puede aparecer en cualquier lugar de una cadena; en el ejemplo, 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 dimensiones Una tabla de dos dimensiones es una tabla en la que Usted elige una fila y una columna y lee el valor de la intersecci´n. Un buen ejemplo es una tabla de o
  • 93. 6.5 Encapsulado y generalizaci´n o 67 multiplicar. Supongamos que desea imprimir una tabla de multiplicar para los valores del 1 al 6. Una buena manera de comenzar es escribir un bucle sencillo que imprima los m´ltiplos de 2, todos en una l´ u ınea. i = 1 while i <= 6: print 2*i, ’t’, i = i + 1 print La primera l´ ınea inicializa una variable lllamada i, que actuar´ como contador, a o variable de bucle. Conforme se ejecuta el bucle, el valor de i se incrementa de 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 e completar el bucle, la segunda sentencia print crea una l´ ınea nueva. La salida de este programa es: 2 4 6 8 10 12 Hasta ahora, bien. El siguiente paso es encapsular y generalizar. 6.5. Encapsulado y generalizaci´n o Por “encapsulado” generalmente se entiende tomar una pieza de c´digo y envol- o verla en una funci´n, permiti´ndole obtener las ventajas de todo aquello para o e lo que valen las funciones. Hemos visto dos ejemplos de encapsulado, cuando escribimos imprimeParidad en la Secci´n 4.5 y esDivisible en la Secci´n 5.4. o o Por “generalizaci´n” entendemos tomar algo espec´ o ıfico, como imprimir los m´lti- u plos de 2, y hacerlo m´s general, como imprimir los m´ltiplos de cualquier entero. a u He aqu´ una funci´n que encapsula el bucle de la secci´n anterior y la generaliza ı o o para imprimir m´ltiplos de n. u def imprimeMultiplos(n): i = 1 while i <= 6: print n*i, ’t’, i = i + 1 print
  • 94. 68 Iteraci´n o Para encapsular, todo lo que hubimos de hacer fue a˜adir la primera l´ n ınea, que declara el nombre de la funci´n y la lista de par´metros. Para generalizar, todo o a lo que tuvimos que hacer fue sustituir el valor 2 por el par´metro n. a Si llamamos a esta funci´n con el argumento 2, obtenemos la misma salida que o antes. Con el argumento 3, la salida es: 3 6 9 12 15 18 y con argumento 4, la salida es 4 8 12 16 20 24 A estas alturas es probable que haya adivinado c´mo vamos a imprimir una o tabla de multiplicaci´n: llamaremos a imprimeMultiplos repetidamente con o diferentes argumentos. De hecho, podemos a usar otro bucle: i = 1 while i <= 6: imprimeMultiplos(i) i = i + 1 Observe hasta qu´ punto este bucle es similar al que hay en el interior de e imprimeMultiplos. Todo lo que hicimos fue sustituir la sentencia print por una llamada a una funci´n. o La 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 36 6.6. M´s encapsulaci´n a o Para dar m´s ejemplos de encapsulaci´n, tomaremos el c´digo del final de la a o o Secci´n 6.5 y lo envolveremos en una funci´n: o o def imprimeTablaMult(): i = 1 while i <= 6: imprimeMultiplos(i) i = i + 1 El 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
  • 95. 6.7 Variables locales 69 el int´rprete. Cuando conseguimos que funcionen, se extraen y se envuelven en e una funci´n. o Este plan de desarrollo es especialmente si, al comenzar a escribir, no sabe c´mo dividir el programa en funciones. Este enfoque le permite dise˜arlo sobre o n la marcha. 6.7. Variables locales Quiz´ se est´ preguntando c´mo podemos usar la misma variable tanto en a e o imprimeMultiplos como en imprimeTablaMult. ¿No habr´ problemas cuan- a do una de las funciones cambie los valores de la variable? La respuesta es no, ya que la variable i en imprimeMultiplos y la variable i in imprimeTablaMult no son la misma variable. Las variables creadas dentro de una funci´n son locales. No puede acceder a una o variable local fuera de su funci´n “hu´sped”. Eso significa que es posible tener o e m´ltiples variables con el mismo nombre, siempre que no est´n en la misma u e funci´n. o El diagrama de pila de esta funci´n muestra claramente que las dos variables o llamadas i no son la misma variable. Pueden referirse a diferentes valores, y cambiar uno no afecta al otro. printMultTable 1 i 2 3 printMultiples 1 n 3 i 2 El 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 a imprimeTablaMult llama a imprimeMultiplos con el valor actual de i como argumento. Ese valor se asigna al par´metro n. a Dentro 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 u de i en imprimeTablaMult. Es habitual y perfectamente legal tener diferentes variables locales con el mismo nombre. En especial, los nombres i, j y k se suelen usar como variables de
  • 96. 70 Iteraci´n o bucle. Si evita usarlas en una funci´n porque las utiliz´ en alg´n otro lugar, o o u probablemente consiga que el programa sea m´s dif´ de leer. a ıcil 6.8. M´s generalizaci´n a o Como otro ejemplo de generalizaci´n, imagine que desea un programa que im- o prima una tabla de multiplicaci´n de cualquier tama˜o, y no s´lo la tabla de o n o 6x6. Podr´ a˜adir un par´metro a imprimeTablaMult: ıa n a def imprimeTablaMult(mayor): i = 1 while i <= mayor: imprimeMultiplos(i) i = i + 1 Hemos sustituido el valor 6 con el par´metro mayor. Si ahora se llama a a imprimeTablaMult 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 42 lo que es correcto, excepto por el hecho de que seguramente queremos que la tabla est´ cuadrada, con el mismo n´mero de filas que de columnas. Para hacer- e u lo, a˜adimos otro par´metro a imprimeMultiplos, a fin de especificar cu´ntas n a a columnas tendr´ que tener la tabla. ıa S´lo para fastidiar, llamaremos tambi´n a este par´metro mayor, para demostrar o e a que diferentes funciones pueden tener par´metros con el mismo nombre (al igual a que 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:
  • 97. 6.9 Funciones 71 imprimeMultiplos(i, mayor) i = i + 1 N´tese que al a˜adir un nuevo par´metro, tuvimos que cambiar la primera l´ o n a ınea de la funci´n (el encabezado de la funci´n), y tuvimos tambi´n que cambiar el o o e lugar donde se llama a la funci´n en imprimeTablaMult. o Seg´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 49 Cuando generaliza correctamente una funci´n, a menudo se encuentra con que o el 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 e manera que todas las entradas de la tabla aparecen dos veces. Puede ahorrar tinta imprimiendo s´lo la mitad de la tabla. Para hacerlo, s´lo tiene que cambiar o o una 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. o 6.9. Funciones Hasta el momento hemos mencionado en alguna ocasi´n “todas las cosas para o las que sirven las funciones”. Puede que ya se est´ preguntando qu´ cosas son e e exactamente. He aqu´ algunas de las razones por las que las funciones son utiles: ı ´
  • 98. 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. Glosario asignaci´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. o iteraci´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. o bucle: Sentencia o grupo de sentencias que se ejecutan repetidamente hasta que se cumple una condici´n de terminaci´n. o o bucle infinito: Bucle cuya condici´n de terminaci´n nunca se cumple. o o cuerpo: 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. o tabulador: 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. a secuencia 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. a encapsular: Dividir un programa largo y complejo en componentes (como las funciones) y aislar los componentes unos de otros (por ejemplo usando variables locales).
  • 99. 6.10 Glosario 73 generalizar: 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 a plan 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.
  • 101. Cap´ ıtulo 7 Cadenas 7.1. Un tipo de datos compuesto Hasta el momento hemos visto tres tipos: int, float, y string. Las cadenas son cuantitativamente diferentes de los otros dos porque est´n hechas de piezas a menores: caracteres. Los tipos que comprenden piezas menores se llaman tipos de datos com- puestos. Dependiendo de qu´ estemos haciendo, podemos querer tratar un tipo e compuesto 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 letra La expresi´n fruta[1] selecciona el car´cter n´mero 1 de fruta. La variable o a u letra apunta al resultado. Cuando mostramos letra, nos encontramos con una sorpresa: a La 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 o contar 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 e Si quiera la cer´sima letra de una cadena, simplemente pone 0, o cualquier o expresi´n de valor 0, entre los corchetes: o
  • 102. 76 Cadenas >>> letra = fruta[0] >>> print letra b A la expresi´n entre corchetes se le llama ´ o ındice. Un ´ ındice identifica a un miembro de un conjunto ordenado, en este caso el conjunto de caracteres de la cadena. El ´ ındice indica cu´l quiere usted, de ah´ el nombre. Puede ser cualquier a ı expresi´n entera. o 7.2. Longitud La funci´n len devuelve el n´mero de caracteres de una cadena: o u >>> fruta = "banana" >>> len(fruta) 6 Para 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 o string 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 a del 0 al 5. Para obtener el ultimo car´cter tenemos que restar 1 de longitud: ´ a longitud = len(fruta) ultima = fruta[longitud-1] De forma alternativa, podemos usar ´ ındices negativos, que cuentan hacia atr´s a desde 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 for Muchos c´lculos incluyen el proceso de una cadena car´cter a car´cter. A me- a a a nudo empiezan por el principio, seleccionan cada car´cter por turno, hacen algo a con ´l y siguen hasta el final. Este patr´n de proceso se llama recorrido. Una e o forma de codificar un recorrido es una sentencia while: indice = 0 while indice < len(fruta): letra = fruta[indice]
  • 103. 7.3 Recorrido y el bucle for 77 print letra indice = indice + 1 Este bucle recorre la cadena y muestra cada letra en una l´ ınea distinta. La condici´n del bucle es indice < len(fruta), de modo que cuando indice es o igual a la longitud de la cadena, la condici´n es falsa y no se ejecuta el cuerpo del o bucle. 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 Python facilita una sintaxis alternativa m´s simple: el bucle for: a for car in fruta: print car Cada vez que recorremos el bucle, se asigna a la variable car el siguiente car´cter a de la cadena. El bucle contin´a hasta que no quedan caracteres. u El ejemplo siguiente muestra c´mo usar la concatenaci´n junto a un bucle for o o para generar una serie abeced´rica. “Abeced´rica” es la serie o lista en la que a a cada uno de los elementos aparece en orden alfab´tico. Por ejemplo, en el libro e de Robert McCloskey Make Way for Ducklings (Dejad paso a los patitos), los nombres 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 + sufijo La salida del programa es: Jack Kack Lack Mack Nack Oack Pack Qack Por supuesto, esto no es del todo correcto, porque “Ouack” y “Quack” no est´n a correctamente escritos.
  • 104. 78 Cadenas Como ejercicio, modifique el programa para corregir este error. 7.4. Porciones de cadenas Llamamos porci´n a un segmento de una cadena. La selecci´n de una porci´n o o o es 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 a hasta 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 6 Si omite el primer ´ ındice (antes de los dos puntos), la porci´n comienza al o principio de la cadena. Si omite el segundo ´ ındice, la porci´n llega al final de la o cadena. As´ı: >>> fruta = "banana" >>> fruta[:3] ’ban’ >>> fruta[3:] ’ana’ ¿Qu´ cree usted que significa s[:]? e 7.5. Comparaci´n de cadenas o Los operadores de comparaci´n trabajan sobre cadenas. Para ver si dos cadenas o son iguales:
  • 105. 7.6 Las cadenas son inmutables 79 if palabra == "banana": print "S´, no tenemos bananas!" ı Otras operaciones de comparaci´n son utiles para poner palabras en orden al- o ´ fab´tico: e if palabra < "banana": print "Tu palabra," + palabra + ", va antes de banana." elif palabra > "banana": print "Tu palabra," + palabra + ", va despu´s de banana." e else: print "S´, no tenemos bananas!" ı Sin embargo, deber´ usted ser consciente de que Python no maneja las may´scu- ıa u las y min´sculas como lo hace la gente. Todas las may´suculas van antes de la u u min´sculas. Como resultado de ello: u Tu palabra, Zapato, va antes de banana. Una forma com´n de abordar este problema es convertir las cadenas a un forma- u to est´ndar, como pueden ser las min´sculas, antes de realizar la comparaci´n. a u o Un problema mayor es hacer que el programa se d´ cuenta de que los zapatos e no son frutas. 7.6. Las cadenas son inmutables Es tentador usar el operador [] en el lado izquierdo de una asignaci´n, con la o intenci´n de cambiar un car´cter en una cadena. Por ejemplo: o a saludo = "Hola, mundo" saludo[0] = ’M’ # ERROR! print saludo En lugar de presentar la salida Mola, mundo, este c´digo presenta el siguien- o te error en tiempo de ejecuci´n TypeError: object doesn’t support item o assignment. 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 a variaci´n de la original: o saludo = "Hola, mundo" nuevoSaludo = ’M’ + saludo[1:] print nuevoSaludo
  • 106. 80 Cadenas Aqu´ la soluci´n es concatenar una nueva primera letra a una porci´n de saludo. ı o o Esta operaci´n no tiene efectos sobre la cadena original. o 7.7. Una funci´n “encuentra” o ¿Qu´ hace la siguiente funci´n? e o def encuentra(cad, c): indice = 0 while indice < len(cad): if cad[indice] == c: return indice indice = indice + 1 return -1 En cierto sentido, encuentra es lo contrario del operador []. En lugar de tomar un ´ındice y extraer el car´cter correspondiente, toma un car´cter y encuentra a a el ´ ındice donde aparece el car´cter. Si el car´cter no se encuentra, la funci´n a a o devuelve -1. Este es el primer ejemplo que hemos visto de una sentencia return dentro de un bucle. Si cad[indice] == c, la funci´n vuelve inmediatamente, escapando o del bucle prematuramente. Si el car´cter no aparece en la cadena, el programa sale del bucle normalmente a y devuelve -1. Este patr´n de computaci´n se llama a veces un recorrido “eureka” porque en o o cuanto encontramos lo que buscamos, podemos gritar “¡Eureka!” y dejar de buscar. 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 conteo El programa que sigue cuenta el n´mero de veces que la letra a aparece en una u cadena:
  • 107. 7.9 El m´dulo “string” o 81 fruta = "banana" cuenta = 0 for car in fruta: if car == ’a’: cuenta = cuenta + 1 print cuenta Este programa muestra otro patr´n de computaci´n llamado contador. La o o variable cuenta se incializa a 0 y luego se incrementa cada vez que se encuentra una a. (Incrementar es aumentar en uno; es lo contario de decrementar, y sin relaci´n alguna con “excremento”, que es un nombre.) Al salir del bucle, o cuenta 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” o El 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 string El m´dulo string incluye una funci´n llamada find que hace lo mismo que la o o funci´n encuentra que escribimos. Para llamarla debemos especificar el nombre o del 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 indice 1 Este ejemplo demuestra uno de los beneficios de los m´dulos: ayudan a evitar o las colisiones entre los nombres de las funciones predefinidas y las definidas por el usuario. Al usar la notaci´n de punto podr´ o ıamos especificar qu´ versi´n de e o find queremos en caso de haberle daddo un nombre en ingl´s a nuestra funci´n. e o En realidad, string.find es m´s general que nuestra versi´n. Para empezar, a o puede encontrar subcadenas, no s´lo caracteres: o
  • 108. 82 Cadenas >>> string.find("banana", "na") 2 Adem´s, acepta un argumento adicional que especifica el ´ a ındice en el que deber´ ıa comenzar: >>> string.find("banana", "na", 3) 4 O puede tomar dos argumentos adicionales que especifican un intervalo de ´ ındi- ces: >>> string.find("sus", "s", 1, 2) -1 En este ejemplo, la b´squeda falla porque la letra s no aparece en el intervalo u de ´ ındices desde 1 hasta 2 (sin incluir 2). 7.10. Clasificaci´n de caracteres o A menudo viene bien examinar un car´cter y comprobar si es una letra may´scu- a u la o min´scula, o si es un car´cter o un d´ u a ıgito. El m´dulo string proporciona o varias 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 u may´sculas. Pruebe lo que sigue y vea qu´ obtiene: u e >>> print string.lowercase >>> print string.uppercase >>> print string.digits Podemos usar estas constantes y find para clasificar caracteres. Por ejemplo, si find(lowercase, c) devuelve un valor que no sea -1, entonces c es una min´scula: u def esMinuscula(c): return find(string.lowercase, c) != -1 Alternativamente, podemos aprovecharnos del operador in, que determina si un car´cter aparece en una cadena: a def esMinuscula(c): return c in string.lowercase
  • 109. 7.11 Glosario 83 Como una alternativa m´s, podemos usar el operador de comparaci´n, aunque a o esta soluci´n s´lo sea pr´ctica para el alfabeto ingl´s: o o a e def 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 o imprima: >>> print string.whitespace Los caracteres de whitespace mueven el cursor sin imprimir nada. Crean los espacios en blanco entre los caracteres visibles (al menos sobre papel blanco). La constante string.whitespace contiene todos los caracteres de espacio en blanco, 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 ´ o un manual de referencia. Por otra parte, la Referencia de la Biblioteca de Python s´ lo es. Junto con un mont´n m´s de documentaci´n, est´ disponible en el sitio ı o a o a web de Python, www.python.org. 7.11. Glosario tipo 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. a porci´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.
  • 110. 84 Cadenas incrementar: 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.
  • 111. Cap´ ıtulo 8 Listas Una 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. Las listas son similares a las cadenas de texto (strings), que son conjuntos ordenados de caracteres, excepto en que los elementos de una lista pueden ser de cualquier tipo. Las listas y las cadenas, y otras cosas que se comportan como conjuntos ordenados, se llaman secuencias. 8.1. Valores de una lista Hay varias maneras de crear una nueva lista; la m´s sencilla es encerrar sus a elementos entre corchetes: [10, 20, 30, 40] ["spam", "el´stico", "golondrina"] a El primer ejemplo es una lista de cuatro enteros. El segundo es una lista de tres cadenas de texto. Los elementos de una lista no tienen por qu´ ser del mismo e tipo. La siguiente lista contiene una cadena, un n´mero con decimales y un u entero, y, maravilla de las maravillas, otra lista: ["hola", 2.0, 5, [10, 20]] Se dice que una lista dentro de otra lista est´ anidada. a Las listas que contienen n´meros enteros consecutivos son comunes, de manera u que Python proporciona una manera sencilla de crearlas: >>> range(1,5) [1, 2, 3, 4]
  • 112. 86 Listas La funci´n range toma dos argumentos y devuelve una lista que contiene todos o los enteros entre el primero y el segundo, ¡incluyendo el primero pero no el segundo! Hay dos formas alternativas para range. Con un solo argumento, crea una lista que 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; a a esto se le llama paso (step). Este ejemplo cuenta de 1 a 10 de dos en dos (con pasos de 2). >>> range(1, 10, 2) [1, 3, 5, 7, 9] Para terminar, hay una lista especial que no contiene elementos. Se la llama lista vac´ y se representa []. ıa Con todas estas maneras para crear listas, ser´ decepcionante que no pudi´ra- ıa e mos asignar valores de listas a variables o pasar listas como par´metros a fun- a ciones. 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 elementos La sintaxis para acceder a los elementos de una lista es la misma que para acceder a los caracteres de una cadena: el operador corchetes []. La expresi´n o dentro de los corchetes especifica el ´ ındice. Recuerde que los ´ındices siempre comienzan en cero: print numeros[0] numeros[1] = 5 El operador [] puede aparecer en cualquier parte de una expresi´n. Cuando o aparece a la izquierda de una asignaci´n, cambia uno de los elementos de la o lista, de manera que el “un´simo” elemento de numeros, que era 123, ahora es e 5. Se puede usar como ´ ındice cualquier expresi´n entera. o
  • 113. 8.3 Longitud (tama˜ o) de una lista n 87 >>> numeros[3-2] 5 >>> numeros[1.0] TypeError: sequence index must be integer Si intenta acceder (leer o modificar) un elemento que no existe, obtendr´ un a error en tiempo de ejecuci´n: o >>> numeros[2] = 5 IndexError: list assignment index out of range Si 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 range numeros[-1] es el ultimo elemento de la lista, numeros[-2] es el pen´ltimo, y ´ u numeros[-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 + 1 Este bucle while cuenta desde 0 hasta 4. Cuando la variable de bucle vale 4, la condici´n falla y acaba el bucle. Por tanto, el cuerpo del bucle s´lo se ejecuta o o cuando 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 o recorrido de lista. 8.3. Longitud (tama˜ o) de una lista n La funci´n len toma una lista y devuelve su tama˜o. Es una buena idea usar o n este valor como l´ ımite superior de un bucle, en lugar de una constante. De esta manera, si el tama˜o de la lista cambia, no habr´ que estar haciendo cambios n a en todos los bucles; funcionar´n correctamente con cualquier tama˜o de lista. a n jinetes = ["guerra", "hambre", "peste", "muerte"] i = 0
  • 114. 88 Listas while i < len(jinetes): print jinetes[i] i = i + 1 La 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 ´ o falla 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 anidada cuenta 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 lista in es un operador booleano que comprueba la pertenencia a una secuencia. Lo usamos en la Secci´n 7.10 con las cadenas, pero tambi´n funciona con las listas o e y otras secuencias: >>> jinetes = [’guerra’, ’hambre’, ’peste’, ’muerte’] >>> ’peste’ in jinetes 1 >>> ’libertinaje’ in jinetes 0 Como “peste” es un miembro de la lista jinetes, el operador in devuelve verdadero. Como “libertinaje” no est´ en la lista, in devuelve falso. a Podemos usar not en combinaci´n con in para comprobar si un elemento no es o miembro de una lista: >>> ’libertinaje’ not in jinetes 1 8.5. Listas y bucles for El bucle for que vimos en la Secci´n 7.3 tambi´n funciona con las listas. La o e sintaxis generalizada de un bucle for es:
  • 115. 8.6 Operaciones con listas 89 for VARIABLE in LISTA: CUERPO Esta sentencia es equivalente a: i = 0 while i < len(LISTA): VARIABLE = LISTA[i] CUERPO i = i + 1 El bucle for es m´s conciso porque podemos eliminar la variable de bucle, i. a Aqu´ tenemos el bucle anterior con un bucle for: ı for jinete in jinetes: print jinete M´s a´n, casi se lee igual que en espa˜ol, “Para (cada) jinete en (la lista de) a u n jinetes, 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 u ejemplo expresa su entusiasmo por diferentes frutas. 8.6. Operaciones con listas El 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 repetido cuatro veces. En el segundo ejemplo, la lista [1, 2, 3] se repite tres veces.
  • 116. 90 Listas 8.7. Porciones (slices) Las operaciones de porciones que vimos en la Secci´n 7.4 tambi´n funcionan en o e sobre 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 mutables A diferencia de las cadenas, las listas son mutables, lo que significa que pode- mos cambiar sus elementos. Podemos modificar uno de sus elementos usando el operador 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 ıa posici´n deseada: o >>> lista = [’a’, ’d’, ’f’] >>> lista[1:1] = [’b’, ’c’] >>> print lista
  • 117. 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 lista El uso de porciones para borrar elementos de una lista puede ser extra˜o, y n por ello propicio a los errores. Python nos da una alternativa que resulta m´s a legible. 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 tiempo de 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 no inclu´ ıdo, el segundo ´ ındice. 8.10. Objetos y valores Si 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 a podemos saber si est´n apuntando a la misma cadena. a Hay dos posibles estados:
  • 118. 92 Listas a "banana" a "banana" b "banana" b En 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 una variable. 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 o al mismo objeto. >>> id(a) 135044008 >>> id(b) 135044008 En este caso, las dos veces obtenemos el mismo identificador, lo que significa que Python s´lo cre´ una cadena y ambas variables, a y b, apuntan a ella. o o Como las cadenas de texto son inmutables, no hay diferencia pr´ctica entre los a dos 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) 135041704 De 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, ambas variables se refieren al mismo objeto:
  • 119. 8.12 Clonar listas 93 >>> a = [1, 2, 3] >>> b = a En este caso, el diagrama de estados ser´ como ´ste: ıa e a [ 1, 2, 3 ] b Como la misma lista tiene dos nombres diferentes, a y b, podemos decir que se le 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- a bles. Por supuesto, no hay problema con los objetos inmutables. Por ello Python se toma la libertad de poner alias a las cadenas cuando ve una oportunidad de economizar. 8.12. Clonar listas Si queremos modificar una lista y mantener una copia del original, necesitaremos ser capaces de hacer una copia de la lista en s´ no s´lo de su referencia. Este ı, o proceso 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 o consta 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.
  • 120. 94 Listas 8.13. Listas como par´meteros a Cuando se pasa una lista como argumento, en realidad se pasa una referencia a ella, no una copia de la lista. Por ejemplo, la funci´n cabeza toma una lista o como 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) 1 El par´metro lista y la variable numeros son alias de un mismo objeto. El a diagrama de estado es as´ ı: __main__ numbers [ 1, 2, 3 ] head list Como el objeto lista est´ compartido por dos marcos, lo dibujamos entre ambos. a Si la funci´n modifica una lista pasada como par´metro, el que hizo la llamada o a ver´ 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, o cola 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]
  • 121. 8.14 Listas anidadas 95 Como el valor de retorno se cre´ con una porci´n, es una lista. La creaci´n de o o o rest, as´ como cualquier cambio posterior en rest, no afectar´ a numbers. ı a 8.14. Listas anidadas Una 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 elementos de la lista anidada, podemos proceder en dos pasos: >>> elt = lista[3] >>> elt[0] 10 O podemos combinarlos: >>> lista[3][1] 20 Los operadores corchete se eval´an de izquierda a derecha, as´ que esta expresi´n u ı o saca el tri-´simo elemento de lista y luego extrae el un´simo elemento de ella. e e 8.15. Matrices Es com´n usar listas anidadas para representar matrices. Por ejemplo, la matriz: u 1 2 3 4 5 6 7 8 9 puede 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 la matriz. 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
  • 122. 96 Listas El primer ´ ındice escoge la fila y el segundo la columna. Aunque esta manera de representar matrices es com´n, no es la unica posibilidad. Una peque˜a variaci´n u ´ n o consiste en usar una lista de columnas en lugar de flas. M´s adelante veremos a una alternativa m´s radical usando un diccionario. a 8.16. Cadenas y listas Dos de las funciones m´s utiles del m´dulo string tienen que ver con listas a ´ o de cadenas. La funci´n split divide una cadena en una lista de palabras. Por o defecto, cualquier n´mero de caracteres de espacio en blanco se considera un u l´ ı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 especificar qu´ caracteres se usar´n como l´ e a ımites de palabra. El siguiente ejemplo usa la cadena 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 o los 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
  • 123. 8.17 Glosario 97 8.17. Glosario lista: 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. u clonar: 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.
  • 125. Cap´ ıtulo 9 Tuplas 9.1. Mutabilidad y tuplas Hasta ahora, ha visto dos tipos compuestos: cadenas, que est´n hechas de ca- a racteres, y listas, que est´n hechas de elementos de cualquier tipo. Una de las a diferencias que se˜alamos es que los elementos de una lista se pueden modifi- n car, pero los caracteres de una cadena no. En otras palabras, las cadenas son inmutables y las listas son mutables. En Python hay otro tipo llamado tupla que es similar a una lista salvo en que es inmutable. Sint´cticamente, una tupla es una lista de valores separados por a comas: >>> tupla = ’a’, ’b’, ’c’, ’d’, ’e’ Aunque no es necesario, la convenci´n dice que hay que encerrar las tuplas entre o par´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’>
  • 126. 100 Tuplas Dejando a un lado las cuestiones de sintaxis, las operaciones sobre las tuplas son las mismas que sobre las listas. El operador ´ ındice selecciona un elemento de 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 un error: >>> tupla[0] = ’A’ TypeError: object doesn’t support item assignment Por 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 o De vez en cuando, es util intercambiar los valores de dos variables. Para ha- ´ cerlo con sentencias de asignaci´n convencionales debemos usar una variable o temporal. Por ejemplo, para intercambiar a y b: >>> temp = a >>> a = b >>> b = temp Si tenemos que hacer esto a menudo, esta aproximaci´n resulta aparatosa. Pyt- o hon proporciona una forma de asignaci´n de tuplas que soluciona este pro- o blema elegantemente: >>> a, b = b, a El lado izquierdo es una tupla de variables, el lado derecho es una tupla de valores. Cada valor se asigna a su respectiva variable. Todas las expresiones del lado derecho se eval´an antes de las asignaciones. Esta caracter´ u ıstica hace de la asignaci´n de tuplas algo muy vers´til. o a
  • 127. 9.3 Tuplas como valor de retorno 101 Naturalmente, el n´mero de variables a la izquierda y el n´mero de valores a la u u derecha deben ser iguales: >>> a, b, c, d = 1, 2, 3 ValueError: unpack tuple of wrong size 9.3. Tuplas como valor de retorno Las funciones pueden devolver tuplas como valor de retorno. Por ejemplo, podr´ ıamos escribir una funci´n que intercambie dos par´metros: o a def intercambio(x, y): return y, x Luego 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.o De hecho, existe un peligro al intentar encapsular intercambio, y es el tentador error que sigue: def intercambio(x, y): # versi´n incorrecta o x, y = y, x Si 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 x se 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 o intentamos. 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. e 9.4. N´ meros aleatorios u La mayor parte de los programas hacen lo mismo cada vez que los ejecutamos, por lo que se dice que son deterministas. Normalmente el determinismo es una cosa buena, ya que esperamos que un c´lculo nos d´ siempre el mismo a e
  • 128. 102 Tuplas resultado. Para algunas aplicaciones, sin embargo, queremos que el computador sea impredecible. El ejemplo obvio son los juegos, pero hay m´s. a Hacer 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 generar n´meros aleatorios y usarlos para determinar el resultado del programa. Python u proporciona una funci´n interna que genera n´meros pseudoaleatorios, que o u no son verdaderamente aleatorios en un sentido matem´tico, pero servir´n para a a nuestros prop´sitos. o El m´dulo random contiene una funci´n llamada random que devuelve un n´mero o o u en coma flotante entre 0,0 y 1,0. Cada vez que usted llama a random obtiene el siguiente n´mero de una larga serie. Para ver un ejemplo, ejecute este bucle: u import random for i in range(10): x = random.random() print x Para 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 u El primer paso es generar una lista de valores aleatorios. listaAleatorios acepta un par´metro entero y devuelve una lista de n´meros aleatorios de la a u longitud 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 u una referencia a la lista completa: def listaAleatorios(n): s = [0] * n for i in range(n): s[i] = random.random() return s Vamos a probar esta funci´n con una lista de ocho elementos. A la hora de o depurar es una buena idea empezar con algo peque˜o. n
  • 129. 9.6 Conteo 103 >>> listaAleatorios(8) 0.15156642489 0.498048560109 0.810894847068 0.360371157682 0.275119183077 0.328578797631 0.759199803101 0.800367163582 Se supone que los n´meros generados por random est´n distribuidos uniforme- u a mente, lo que significa que cada valor es igualmente probable. Si dividimos el intervalo de valores posibles en “baldes” de igual tama˜o y n contamos el n´mero de veces que un valor cae en cada balde, deber´ u ıamos tener m´s o menos el mismo n´mero en todos. a u Podemos contrastar esta teor´ escribiendo un programa que divida el intervalo ıa en baldes y contando el n´mero de valores en cada uno. u 9.6. Conteo Un buen enfoque sobre problemas como ´ste es dividir el problema en subpro- e blemas 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 u veces que un valor cae en un intervalo dado. Eso nos suena. En la Secci´n 7.8 o escribimos un programa que recorr´ una cadena de texto y contaba el n´mero ıa u de veces que aparec´ una letra determinada. ıa As´ podemos hacerlo copiando el programa viejo y adapt´ndolo al problema ı, a actual. El programa original era: cuenta = 0 for car in fruta: if car == ’a’: cuenta = cuenta + 1 print cuenta El primer paso es sustituir fruta con lista y car con num. Esto no cambia el programa, s´lo lo hace m´s legible. o a El segundo paso es cambiar la comprobaci´n. No estamos interesados en encon- o trar letras. Queremos ver si num est´ entre los valores de minimo y maximo. a
  • 130. 104 Tuplas cuenta = 0 for num in lista if minimo < num < maximo: cuenta = cuenta + 1 print cuenta El ultimo paso es encapsular este c´digo en una funci´n llamada enElBalde. ´ o o Los par´metros son la lista y los valores minimo y maximo. a def enElBalde(lista, minimo, maximo): cuenta = 0 for num in lista: if minimo < num < maximo: cuenta = cuenta + 1 return cuenta Copiar y modificar un programa existente nos facilita escribir esta funci´n r´pi- o a damente y nos ahorra un mont´n de tiempo de depuraci´n. Este plan de desa- o o rrollo se llama coincidencia de esquemas. Si se encuentra trabajando en un problema que ya solucion´, reutilice la soluci´n. o o 9.7. Muchos baldes Tal como aumenta el n´mero de baldes, enElBalde se hace un tanto dif´ de u ıcil manejar. Con dos baldes, no est´ mal: a bajo = 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 de cada balde. Empezaremos por solucionar el segundo problema. Si el n´mero de baldes es u numBaldes, la anchura de cada balde es 1.0 / numBaldes. Usaremos un bucle para calcular el intervalo de cada balde. La variable del bucle, i, cuenta de 1 a numBaldes-1:
  • 131. 9.7 Muchos baldes 105 anchuraBalde = 1.0 / numBaldes for i in range(numBaldes): minimo = i * anchuraBalde maximo = minimo + anchuraBalde print minimo, "hasta", maximo Para calcular el l´ ımite inferior de cada balde, multiplicamos la variable de bucle por la anchura de balde. El l´ ımite superior est´ a tan s´lo una anchuraBalde. a o Con numBaldes = 8, la salida es: 0.0 hasta 0.125 0.125 hasta 0.25 0.25 hasta 0.375 0.375 hasta 0.5 0.5 hasta 0.625 0.625 hasta 0.75 0.75 hasta 0.875 0.875 hasta 1.0 Puede confirmar que todos los bucles tienen la misma anchura, que no se solapan y que cubren todo el intervalo entre 0,0 y 1,0. Volvamos ahora al primer problema. Necesitamos un modo de almacenar ocho enteros, usando la variable de bucle para se˜alarlos uno por uno. En estos mo- n mentos deber´ usted estar pensando “¡Lista!”. ıa Debemos crear la lista de baldes fuera del bucle, porque s´lo queremos hacer- o lo una vez. Dentro del bucle, podemos llamar repetidamente a enElBalde y actualizar el i-´simo elemento de la lista: e numBaldes = 8 baldes = [0] * numBaldes anchuraBalde = 1.0 / numBaldes for i in range(numBaldes): minimo = i * anchuraBalde maximo = minimo + anchuraBalde baldes[i] = enElBalde(lista, minimo, maximo) print baldes Con 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 a Por lo menos, est´n lo bastante cerca como para que podamos pensar que el a generador de n´meros aleatorios funciona. u
  • 132. 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. u 9.8. Una soluci´n en una sola pasada o Aunque este programa funciona, no es tan eficiente como podr´ ser. Cada vez ıa que llama a enElBalde recorre la lista entera. Con el aumento del n´mero de u baldes, llega a ser un mont´n de recorridos. o Ser´ mejor hacer una sola pasada por la lista y calcular para cada valor el ´ ıa ındice del 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 la anchuraBalde 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 que cae. Como el problema es el inverso del anterior, podemos suponer que deber´ ıamos dividir por anchuraBalde en lugar de multiplicar. La suposici´n es correcta. o Como anchuraBalde = 1.0 / numBaldes, dividir por anchuraBalde es lo mis- mo que multiplicar por numBaldes. Si multiplicamos un n´mero del intervalo u que va de 0,0 a 1,0 por numBaldes, obtenemos un n´mero del intervalo entre u 0,0 y numBaldes. Si redondeamos ese n´mero al entero inferior obtendremos u exactamente lo que estamos buscando, un ´ ındice de balde: numBaldes = 8 baldes = [0] * numBaldes for i in lista: indice = int(i * numBaldes) baldes[indice] = baldes[indice] + 1 Usamos 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 e negativo como mayor que len(baldes)-1)? Una lista como baldes que contiene conteos del n´mero de valores en cada u intervalo 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
  • 133. 9.9 Glosario 107 9.9. Glosario tipo 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. a histograma: 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
  • 135. Cap´ ıtulo 10 Diccionarios Los 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 a error. Los diccionarios son similares a otros tipos compuestos excepto en que pueden usar como ´ındice cualquier tipo inmutable. A modo de ejemplo, crearemos un diccionario 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 n elementos. 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- o naciones a˜aden nuevos elementos al diccionario. Podemos presentar el valor n actual del diccionario del modo habitual: >>> print ing_a_esp {’one’: ’uno’, ’two’: ’dos’} Los elementos de un diccionario aparecen en una lista separada por comas. Cada entrada contiene un ´ ındice y un valor separado por dos puntos (:). En un diccionario, los ´ ındices se llaman claves, por eso los elementos se llaman pares clave-valor. Otra forma de crear un diccionario es dando una lista de pares clave-valor con la misma sintaxis que la salida del ejemplo anterior:
  • 136. 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- a cuparnos por el orden, ya que los elementos de un diccionario nunca se indexan con ´ ındices enteros. En lugar de eso, usamos las claves para buscar los valores correspondientes: >>> 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 diccionarios La sentencia del elimina un par clave-valor de un diccionario. Por ejemplo, el diccionario siguiente contiene los nombres de varias frutas y el n´mero de esas u frutas 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- a ventario 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 u clave-valor: >>> len(inventario) 4
  • 137. 10.2 M´todos del diccionario e 111 10.2. M´todos del diccionario e Un m´todo es similar a una funci´n, acepta par´metros y devuelve un valor, e o a pero la sintaxis es diferente. Por ejemplo, el m´todo keys acepta un diccionario e y devuelve una lista con las claves que aparecen, pero en lugar de la sintaxis de la 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 o nombre del objeto al que se va a aplicar la funci´n, ing a esp. Los par´ntesis o e indican que este m´todo no admite par´metros. e a La llamda a un m´todo se denomina invocaci´n; en este caso, dir´ e o ıamos que estamos 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 e del 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 e la lista son tuplas. Si un m´todo acepta un argumento, usa la misma sintaxis que una llamada e a una funci´n. Por ejemplo, el m´todo has key acepta una clave y devuelve o e verdadero (1) si la clave aparece en el diccionario: >>> ing_a_esp.has_key(’one’) 1 >>> ing_a_esp.has_key(’deux’) 0 Si usted invoca un m´todo sin especificar un objeto, provoca un error. En este e caso, el mensaje de error no es de mucha ayuda: >>> has_key(’one’) NameError: has_key
  • 138. 112 Diccionarios 10.3. Asignaci´n de alias y copiado o Debe 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 el m´todo copy. Por ejemplo, opuestos es un diccionario que contiene pares de e opuestos: >>> 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 copia nueva del mismo diccionario. Si modificamos alias, opuestos tambi´n resulta e cambiado: >>> alias[’derecho’] = ’sentado’ >>> opuestos[’derecho’] ’sentado’ Si modificamos copia, opuestos no var´ ıa: >>> copia[’derecho’] = ’privilegio’ >>> opuestos[’derecho’] ’sentado’ 10.4. Matrices dispersas En la Secci´n 8.14 usamos una lista de listas para representar una matriz. Es o una buena opci´n para una matriz en la que la mayor´ de los valores es diferente o ıa de 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 0 La representaci´n de la lista contiene un mont´n de ceros: o o matriz = [ [0,0,0,1,0], [0,0,0,0,0],
  • 139. 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 o misma 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 o de cero. Cada clave es una tupla, y cada valor es un entero. Para acceder a un elemento de la matriz, podemos usar el operador []: matriz[0,3] 1 F´ıjese en que la sintaxis para la representaci´n por medio del diccionario no es o la 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 error porque 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) 1 El primer argumento es la clave; el segundo argumento es el valor que debe devolver get en caso de que la clave no est´ en el diccionario: e >>> matriz.get((1,3), 0) 0 get mejora sensiblemente la sem´ntica del acceso a una matriz dispersa. L´stima a a de sintaxis. 10.5. Pistas Si estuvo jugando con la funci´n fibonacci de la Secci´n 5.7, es posible que o o haya notado que cuanto m´s grande es el argumento que le da, m´s tiempo le a a
  • 140. 114 Diccionarios cuesta ejecutarse. M´s a´n, el tiempo de ejecuci´n aumenta muy r´pidamente. a u o a En nuestra m´quina, fibonacci(20) acaba instant´neamente, fibonacci(30) a a tarda m´s o menos un segundo, y fibonacci(40) tarda una eternidad. a Para entender por qu´, observe este gr´fico de llamadas de fibonacci con e a n=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 0 Un gr´fico de llamadas muestra un conjunto de cajas de funci´n con l´ a o ıneas que conectan cada caja con las cajas de las funciones a las que llama. En lo alto del gr´fico, fibonacci con n=4 llama a fibonacci con n=3 y n=2. A su vez, a fibonacci 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 o ineficaz 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 o almacen´ndolos en un diccionario. A un valor que ya ha sido calculado y alma- a cenado para un uso posterior se le llama pista. Aqu´ hay una implementaci´n ı o de 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 nuevoValor El 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 o a 1 y 1 corresponde a 1.
  • 141. 10.6 Enteros largos 115 Siempre 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 ı, o sin hacer m´s llamadas recursivas. Si no, tiene que calcular el nuevo valor. El a nuevo valor se a˜ade al diccionario antes de que la funci´n vuelva. n o Con esta versi´n de fibonacci, nuestra m´quina puede calcular fibonacci(40) o a en un abrir y cerrar de ojos. Pero cuando intentamos calcular fibonacci(50), nos encontramos con otro problema: >>> fibonacci(50) OverflowError: integer addition La respuesta, como ver´ en un momento, es 20.365.011.074. El problema es a que este n´mero es demasiado grande para caber en un entero de Python. Se u desborda. Afortunadamente, hay una soluci´n f´cil para este problema. o a 10.6. Enteros largos Python proporciona un tipo llamado long int que puede manejar enteros de cualquier tama˜o. Hay dos formas de crear un valor long int. Una es escribir n un 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 o cualquier tipo num´rico e incluso cadenas de d´ e ıgitos: >>> long(1) 1L >>> long(3.9) 3L >>> long(’57’) 57L Todas 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) 20365011074L Simplemente cambiando el contenido inicial de anteriores cambiamos el com- portamiento de fibonacci. Los primeros dos n´meros de la secuencia son long u ints, as´ que todos los n´meros subsiguientes lo ser´n tambi´n. ı u a e
  • 142. 116 Diccionarios Como ejercicio, modifique factorial de forma que produzca un long int como resultado. 10.7. Contar letras En el cap´ıtulo 7 escribimos una funci´n que contaba el n´mero de apariciones o u de una letra en una cadena. Una versi´n m´s gen´rica de este problema es crear o a e un histograma de las letras de la cadena, o sea, cu´ntas veces aparece cada letra. a Ese 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 a para 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, el diccionario contiene pares de letras y sus frecuencias. Puede ser m´s atractivo mostrar el histograma en orden alfab´tico. Podemos a e hacerlo 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 e a listas que hemos visto. Hay varios m´s, como append, extend, y reverse. a Consulte la documentaci´n de Python para ver los detalles. o 10.8. Glosario diccionario: 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.
  • 143. 10.8 Glosario 117 clave: 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”. o m´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. e pista: 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
  • 145. Cap´ ıtulo 11 Archivos y excepciones Cuando un programa se est´ ejecutando, sus datos est´n en la memoria. Cuando a a un programa termina, o se apaga el computador, los datos de la memoria desa- parecen. Para almacenar los datos de forma permanente debe usted ponerlos en un archivo. Normalmente los archivos se guardan en un disco duro, disquete o CD-ROM. Cuando hay un gran n´mero de archivos, suelen estar organizados en directo- u rios (tambi´n llamados “carpetas”). Cada archivo se identifica con un nombre e unico, o una combinaci´n de nombre de archivo y nombre de directorio. ´ o Leyendo y escribiendo archivos, los programas pueden intercambiar informaci´n o entre ellos y generar formatos imprimibles como PDF. Trabajar con archivos se parece mucho a trabajar con libros. Para usar un libro, tiene que abrirlo. Cuando ha terminado, tiene que cerrarlo. Mientras el libro est´ abierto, puede escribir en ´l o leer de ´l. En cualquier caso, sabe en a e e qu´ lugar del libro se encuentra. Casi siempre lee el libro seg´n su orden natural, e u pero tambi´n puede ir saltando de una p´gina a otra. e a Todo esto sirve tambi´n para los archivos. Para abrir un archivo, especifique su e nombre e indique si quiere leer o escribir. La apertura de un archivo crea un objeto archivo. En este ejemplo, la variable f apunta al nuevo objeto archivo. >>> f = open("test.dat","w") >>> print f <open file ’test.dat’, mode ’w’ at fe820>
  • 146. 120 Archivos y excepciones La funci´n open toma dos argumentos. El primero es el nombre del archivo y o el segundo es el modo. El modo ’w’ (write) significa que lo estamos abriendo para escribir. Si no hay un archivo llamado test.dat se crear´. Si ya hay uno, el archivo que a estamos escribiendo lo reemplazar´. a Al imprimir el objeto archivo, vemos el nombre del archivo, el modo y la loca- lizaci´n del objeto. o Para meter datos en el archivo invocamos al m´todo write sobre el objeto e archivo: >>> 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 deja el archivo listo para leer: >>> f.close() Ya podemos abrir el archivo de nuevo, esta vez para lectura, y poner su contenido en una cadena. Esta vez el argumento de modo es ’r’ (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 e el archivo completo: >>> text = f.read() >>> print text Ya es horade cerrar el archivo No hay un espacio entre “hora” y “de” porque no escribimos un espacio entre las cadenas. read tambi´n puede aceptar un argumento que le indica cu´ntos caracteres leer: e a >>> f = open("test.dat","r") >>> print f.read(7) Ya es h Si no quedan suficientes caracteres en el archivo, read devuelve los que haya. Cuando llegamos al final del archivo, read devuelve una cadena vac´ ıa:
  • 147. 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 o cincuenta 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() return La sentencia break es nueva. Su ejecuci´n interrumpe el bucle; el flujo de la o ejecuci´n pasa a la primera sentencia tras el bucle. o En 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 texto Un 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. Como Python est´ dise˜ado espec´ a n ıficamente para procesar archivos de texto, propor- ciona m´todos que facilitan la tarea. e Para hacer una demostraci´n, crearemos un archivo de texto con tres l´ o ıneas de texto 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 e de l´ ınea:
  • 148. 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 a aparecen con comillas y el car´cter de salto de l´ a ınea aparece como la secuencia de escape 012. Al final del archivo, readline devuelve una cadena vac´ y readlines devuelve ıa una lista vac´ ıa: >>> print f.readline() >>> print f.readlines() [] Lo que sigue es un ejemplo de un programa de proceso de l´ ıneas. filtraArchivo hace 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() return La sentencia continue termina la iteraci´n actual del bucle, pero sigue haciendo o bucles. El flujo de ejecuci´n pasa al principio del bucle, comprueba la condici´n o o y contin´a en consecuencia. u
  • 149. 11.2 Escribir variables 123 As´ si texto es una cadena vac´ el bucle termina. Si el primer car´cter de ı, ıa, a texto es una almohadilla, el flujo de ejecuci´n va al principio del bucle. S´lo si o o ambas condiciones fallan copiamos texto en el archivo nuevo. 11.2. Escribir variables El 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 a f´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 o el operador de formato. El primer operando es la cadena de formato, y el segundo operando es una tupla de expresiones. El resultado es una cadena que contiene los valores de las expresiones, formateados de acuerdo a la cadena de formato. A modo de ejemplo simple, la secuencia de formato ’%d’ significa que la primera 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 de formato, 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 como un n´mero en coma flotante, y ’%s’ formatea el siguiente elemento como una u cadena: >>> "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
  • 150. 124 Archivos y excepciones Por 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 u secuencias de formato de la cadena. Igualmente, los tipos de las expresiones deben coincidir con las secuencias de formato: >>> "%d %d %d" % (1,2) TypeError: not enough arguments for format string >>> "%d" % ’d´lares’ o TypeError: illegal argument type for built-in operation En el primer ejemplo, no hay suficientes expresiones; en el segundo, la expresi´n o es de un tipo incorrecto. Para tener m´s control sobre el formato de los n´meros, podemos detallar el a u n´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 que ocupar´ el n´mero. Si el valor necesita menos d´ a u ıgitos, se a˜aden espacios en n blanco delante del n´mero. Si el n´mero de espacios es negativo, se a˜aden los u u n espacios 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 u flotante: >>> "%12.2f" % 6.1 ’ 6.10’ En este ejemplo, el resultado ocupa doce espacios e incluye dos d´ ıgitos tras la coma. Este formato es util para imprimir cantidades de dinero con las comas ´ alineadas. Imagine, por ejemplo, un diccionario que contiene los nombres de los estudiantes como clave y las tarifas horarias como valores. He aqu´ una funci´n que imprime ı o el contenido del diccionario como un informe formateado: def informe (tarifas) : estudiantes = tarifas.keys() estudiantes.sort()
  • 151. 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 n contenido: >>> tarifas = {’mar´a’: 6.23, ’jos´’: 5.45, ’jes´s’: 4.25} ı e u >>> informe (tarifas) jos´ e 5.45 jes´s u 4.25 mar´a ı 6.23 Controlando la anchura de cada valor nos aseguramos de que las columnas van a quedar alineadas, siempre que los nombres tengan menos de veinti´n caracteres u y las tarifas sean menos de mil millones la hora. 11.3. Directorios Cuando usted crea un archivo nuevo abri´ndolo y escribiendo, el nuevo archi- e vo va al directorio en uso (aqu´l en el que etuviese al ejecutar el programa). e Del mismo modo, cuando abre un archivo para leerlo, Python lo busca en el directorio en uso. Si quiere abrir un archivo de cualquier otro sitio, tiene que especificar la ruta del archivo, que es el nombre del directorio (o carpeta) donde se encuentra ´ste: e >>> f = open("/usr/share/dict/words","r") >>> print f.readline() Aarhus Este ejemplo abre un archivo llamado words que est´ en un directorio llamado a dict, que est´ en share, que est´ en usr, que est´ en el directorio de nivel a a a superior del sistema, llamado /. No puede usar / como parte del nombre de un archivo; est´ reservado como a delimitador entre nombres de archivo y directorios. El archivo /usr/share/dict/words contiene una lista de palabras en orden alfab´tico, la primera de las cuales es el nombre de una universidad danesa. e 11.4. Encurtido Para poner valores en un archivo, debe convertirlos en cadenas. Ya ha visto c´mo hacerlo con str: o
  • 152. 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. Ha perdido la informaci´n del tipo de dato original. En realidad, no puede distinguir o d´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 o y 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 e archivo 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 que volcamos ah´ ı: >>> f = open("test.pck","r") >>> x = pickle.load(f) >>> x 12.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 su tipo original. 11.5. Excepciones Siempre que ocurre un error en tiempo de ejecuci´n, se crea una excepci´n. o o Normalmente el programa se para y Pythton presenta un mensaje de error.
  • 153. 11.5 Excepciones 127 Por ejemplo, la divisi´n por cero crea una excepci´n: o o >>> print 55/0 ZeroDivisionError: integer division or modulo Un elemento no existente en una lista hace lo mismo: >>> a = [] >>> print a[5] IndexError: list index out of range O el acceso a una clave que no est´ en el diccionario: a >>> b = {} >>> print b[’qu´’] e KeyError: qu´ e En cada caso, el mensaje de error tiene dos partes: el tipo de error antes de los dos puntos y detalles sobre el error depu´s de los dos puntos. Normalmente e Python tambi´n imprime una traza de d´nde se encontraba el programa, pero e o la hemos omitido en los ejemplos. A veces queremos realizar una operaci´n que podr´ provocar una excepci´n, o ıa o pero no queremos que se pare el programa. Podemos manejar la excepci´n o usando las sentencias try y except. Por ejemplo, podemos preguntar al usuario por el nombre de un archivo y luego intentar abrirlo. Si el archivo no existe, no queremos que el programa se pare; queremos manejar la excepci´n. o nombreArch = raw_input(’Introduce un nombre de archivo: ’) try: f = open (nombreArch, "r") except: print ’No hay ning´n archivo que se llame’, nombreArch u La 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 o ejecuta las sentencias de la rama except y despu´s contin´a. e u Podemos encapsular esta capacidad en una funci´n: existe acepta un nombre o de archivo y devuelve verdadero si el archivo existe y falso si no: def existe(nombreArch): try: f = open(nombreArch) f.close()
  • 154. 128 Archivos y excepciones return 1 except: return 0 Puede usar m´ltiples bloques except para manejar diferentes tipos de excep- u ciones. El Manual de Referencia de Python contiene los detalles. Si su programa detecta una condici´n de error, puede hacer que lance (raise en o ingl´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 a cualquier raz´n, lanzamos una excepci´n. o o def 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 x La sentencia raise acepta dos argumentos: el tipo de excepci´n e informaci´n o o espec´ ıfica acerca del error. ErrorN´meroMalo es un nuevo tipo de excepci´n que u o hemos inventado para esta aplicaci´n. o Si la funci´n llamada tomaNumero maneja el error, el programa puede continuar; o en caso contrario, Python imprime el mensaje de error y sale: >>> tomaNumero () Elige un n´mero: 17 u ErrorN´meroMalo: 17 es un mal n´mero u u El mensaje de error incluye el tipo de excepci´n y la informaci´n adicional que o o usted 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 u 11.6. Glosario archivo: 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 e ruta: Una secuencia de nombres de directorio que especifica la localizaci´n o exacta de un archivo.
  • 155. 11.6 Glosario 129 archivo 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. o operador 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. o secuencia de formato: Una secuencia de caracteres que comienza con % e in- dica c´mo formatear un valor. o encurtir: 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. a excepci´n: Un error que ocurre en tiempo de ejecuci´n. o o manejar: 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
  • 157. Cap´ ıtulo 12 Clases y objetos 12.1. Tipos compuestos definidos por el usuario Una vez utilizados algunos de los tipos internos de Python, estamos listos para crear un tipo definido por el usuario: el Punto. Piense en el concepto de un punto matem´tico. En dos dimensiones, un punto es a dos n´meros (coordenadas) que se tratan colectivamente como un solo objeto. u En notaci´n matem´tica, los puntos suelen escribirse entre par´ntesis con una o a e coma 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 el origen. Una forma natural de representar un punto en Python es con dos valores en coma flotante. La cuesti´n es, entonces, c´mo agrupar esos dos valores en un o o objeto compuesto. La soluci´n r´pida y burda es utilizar una lista o tupla, y o a para algunas aplicaciones esa podr´ ser la mejor opci´n. ıa o Una alternativa es que el usuario defina un nuevo tipo compuesto, tambi´n e llamado una clase. Esta aproximaci´n exige un poco m´s de esfuerzo, pero o a tiene sus ventajas que pronto se har´n evidentes. a Una definici´n de clase se parece a esto: o class Punto: pass Las definiciones de clase pueden aparecer en cualquier lugar de un programa, pero normalmente est´n al principio (tras las sentencias import). Las reglas a
  • 158. 132 Clases y objetos sint´cticas de la definici´n de clases son las mismas que para cualesquiera otras a o sentencias compuestas. (ver la Secci´n 4.4). o Esta definici´n crea una nueva clase llamada Punto. La sentencia pass no tiene o efectos; s´lo es necesaria porque una sentencia compuesta debe tener algo en su o cuerpo. Al crear la clase Punto hemos creado un nuevo tipo, que tambi´n se llama Punto. e Los miembros de este tipo se llaman instancias del tipo u objetos. La creaci´n o de una nueva instancia se llama instanciaci´n. Para instanciar un objeto Punto o ejecutamos una funci´n que se llama (lo ha adivinado) Punto: o blanco = Punto() A la variable blanco se le asigna una referencia a un nuevo objeto Punto. A una funci´n como Punto que crea un objeto nuevo se le llama constructor. o 12.2. Atributos Podemos a˜adir nuevos datos a una instancia utilizando la notaci´n de punto: n o >>> blanco.x = 3.0 >>> blanco.y = 4.0 Esta sintaxis es similar a la sintaxis para seleccionar una variable de un m´du- o lo, como math.pi o string.uppercase. En este caso, sin embargo, estamos seleccionando un dato de una instancia. Estos ´ ıtemes con nombre se llaman atributos. El diagrama de estados que sigue muestra el resultado de esas asignaciones: blanco x 3.0 y 4.0 La variable blanco apunta a un objeto Punto, que contiene dos atributos. Cada atributo apunta a un n´mero en coma flotante. u Podemos leer el valor de un atributo utilizando la misma sintaxis: >>> print blanco.y 4.0 >>> x = blanco.x >>> print x 3.0
  • 159. 12.3 Instancias como par´metro a 133 La expresi´n blanco.x significa, “ve al objeto al que apunta blanco y toma el o valor de x”. En este caso, asignamos ese valor a una variable llamada x. No hay conflicto entre la variable x y el atributo x. El prop´sito de la notaci´n de punto o o es identificar de forma inequ´ ıvoca a qu´ variable se refiere. e Puede 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.y La 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 a objeto 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. u 12.3. Instancias como par´metro a Puede usted pasar una instancia como par´metro de la forma habitual. Por a ejemplo: def imprimePunto(p): print ’(’ + str(p.x) + ’, ’ + str(p.y) + ’)’ imprimePunto acepta un punto como argumento y lo muestra en formato est´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
  • 160. 134 Clases y objetos 12.4. Mismidad El significado de la palabra “mismo” parece totalmente claro hasta que uno se para un poco a pensarlo, y entonces se da cuenta de que hay algo m´s de lo que a supon´ıa. Por ejemplo, si dice “Pepe y yo tenemos la misma moto”, lo que quiere decir es que su moto y la de usted son de la misma marca y modelo, pero que son dos motos distintas. Si dice “Pepe y yo tenemos la misma madre”, quiere decir que su madre y la de usted son la misma persona1 . As´ que la idea de “identidad” ı es diferente seg´n el contexto. u Cuando habla de objetos, hay una ambig¨edad parecida. Por ejemplo, si dos u Puntos son el mismo, ¿significa que contienen los mismos datos (coordenadas) o que son de verdad el mismo objeto? Para averiguar si dos referencias se refieren al mismo objeto, utilice el operador ==. Por ejemplo: >>> p1 = Punto() >>> p1.x = 3 >>> p1.y = 4 >>> p2 = Punto() >>> p2.x = 3 >>> p2.y = 4 >>> p1 == p2 0 Aunque p1 y p2 contienen las mismas coordenadas, no son el mismo objeto. Si asignamos p1 a p2, las dos variables son alias del mismo objeto: >>> p2 = p1 >>> p1 == p2 1 Este tipo de igualdad se llama igualdad superficial porque s´lo compara las o referencias, pero no el contenido de los objetos. Para comparar los contenidos de los objetos (igualdad profunda) podemos escribir una funci´n llamada mismoPunto: o def 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 a diferentes para los diferentes tipos de identidad. “Misma moto” en este contexto ser´ “gleiche ıa Motorrad” y “misma madre” ser´ “selbe Mutter”. ıa
  • 161. 12.5 Rect´ngulos a 135 Si 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) 1 Por supuesto, si las dos variables apuntan al mismo objeto mismoPunto devuelve verdadero. 12.5. Rect´ngulos a Digamos 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 a simplificar las cosas, supongamos que el rect´ngulo est´ orientado vertical u a a horizontalmente, nunca en diagonal. Tenemos varias posibilidades: podemos se˜alar el centro del rect´ngulo (dos n a coordenadas) y su tama˜o (anchura y altura); o podemos se˜alar una de las n n esquinas y el tama˜o; o podemos se˜alar dos esquinas opuestas. Un modo con- n n vencional es se˜alar la esquina superior izquierda del rect´ngulo y el tama˜o. n a n De nuevo, definiremos una nueva clase: class Rectangulo: # Prohibidos los acentos fuera de las cadenas! pass Y la instanciaremos: caja = Rectangulo() caja.anchura = 100.0 caja.altura = 200.0 Este c´digo crea un nuevo objeto Rectangulo con dos atributos en coma flo- o tante. ¡Para se˜alar la esquina superior izquierda podemos incrustar un objeto n dentro de otro! caja.esquina = Punto() caja.esquina.x = 0.0; caja.esquina.y = 0.0;
  • 162. 136 Clases y objetos El operador punto compone. La expresi´n caja.esquina.x significa “ve al ob- o jeto al que se refiere caja y selecciona el atributo llamado esquina; entonces ve a 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.0 12.6. Instancias como valores de retorno Las 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 p Para llamar a esta funci´n, pase caja como argumento y asigne el resultado a o una variable: >>> centro = encuentraCentro(caja) >>> imprimePunto(centro) (50.0, 100.0) 12.7. Los objetos son mudables Podemos cambiar el estado de un objeto efectuando una asignaci´n sobre uno o de sus atributos. Por ejemplo, para cambiar el tama˜o de un rect´ngulo sin n a cambiar su posici´n, podemos cambiar los valores de anchura y altura: o caja.anchura = caja.anchura + 50 caja.altura = caja.altura + 100 Podemos encapsular este c´digo en un m´todo y generalizarlo para agrandar el o e rect´ngulo en cualquier cantidad: a
  • 163. 12.8 Copiado 137 def agrandaRect(caja, danchura, daltura) : caja.anchura = caja.anchura + danchura caja.altura = caja.altura + daltura Las variables danchura y daltura indican cu´nto debe agrandarse el rect´ngulo a a en cada direcci´n. Invocar este m´todo tiene el efecto de modificar el Rectangulo o e que se pasa como argumento. Por ejemplo, podemos crear un nuevo Rectangulo llamado bob y pas´rselo a a agrandaRect: >>> 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 a Cualquier 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. n 12.8. Copiado El uso de alias puede hacer que un programa sea dif´ de leer, porque los ıcil cambios hechos en un lugar pueden tener efectos inesperados en otro lugar. Es dif´ estar al tanto de todas las variables a las que puede apuntar un objeto ıcil dado. Copiar un objeto es, muchas veces, una alternativa a la creaci´n de un alias. El o m´dulo copy contiene una funci´n llamada copy que puede duplicar cualquier o o objeto: >>> import copy >>> p1 = Punto() >>> p1.x = 3 >>> p1.y = 4 >>> p2 = copy.copy(p1) >>> p1 == p2
  • 164. 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
  • 165. 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. Glosario clase: 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. e atributo: 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
  • 167. Cap´ ıtulo 13 Clases y funciones 13.1. Hora Como otro ejemplo de un tipo definido por el usuario, definiremos una clase llamada Hora que registra la hora del d´ La definici´n de la clase es como ıa. o sigue: class Hora: pass Podemos crear un nuevo objeto Hora y asignar atributos para contener las horas, minutos y segundos: hora = Hora() hora.horas = 11 hora.minutos = 59 hora.segundos = 30 El 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.
  • 168. 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 puras En las pr´ximas secciones, escribiremos dos versiones de una funci´n llamada o o sumaHora que calcule la suma de dos Horas. Mostrar´n dos tipos de funciones: a funciones 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 suma La funci´n crea un nuevo objeto Hora, inicializa sus atributos y devuelve una o referencia al nuevo objeto. A esto se le llama funci´n pura porque no modifica o ninguno de los objetos que se le pasan y no tiene efectos laterales, como mostrar un valor o tomar una entrada del usuario. Aqu´ tiene un ejemplo de c´mo usar esta funci´n. Crearemos dos objetos Hora: ı o o horaActual, que contiene la hora actual, y horaPan, que contiene la cantidad de tiempo que necesita un panadero para hacer pan. Luego usaremos sumaHora para averiguar cu´ndo estar´ hecho el pan. Si a´n no ha terminado de escribir a a u imprimeHora, 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)
  • 169. 13.3 Modificadores 143 La salida de este programa es 12:49:30, lo que es correcto. Por otra parte, hay casos 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 u segundos o minutos suma m´s que sesenta. Cuando ocurre eso, debemos “llevar” a los segundos sobrantes a la columna de los minutos o los minutos extras a la columna de las horas. He aqu´ una versi´n corregida de la funci´n: ı o o def 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 suma Aunque esta funci´n es correcta, empieza a ser grande. M´s adelante sugeriremos o a una aproximaci´n alternativa que nos dar´ un c´digo m´s corto. o a o a 13.3. Modificadores Hay veces en las que es util que una funci´n modifique uno o m´s de los objetos ´ o a que recibe como par´metros. Normalmente, el llamante conserva una referencia a a los objetos que pasa, as´ que cualquier cambio que la funci´n haga ser´ visible ı o a para 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 u escribir´ de forma natural como un modificador. Un esbozo r´pido de la funci´n ıa a o podr´ ser ´ste: ıa e
  • 170. 144 Clases y funciones def 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 + 1 La primera l´ınea realiza la operaci´n b´sica, las restantes tratan con los casos o a especiales que vimos antes. ¿Es correcta esta funci´n? ¿Qu´ ocurre si el par´metro segundos es mucho o e a mayor que sesenta? En tal caso, no es suficiente con acarrear una vez; debemos seguir haci´ndolo hasta que segundos sea menor que sesenta. Una soluci´n es e o sustituir 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 + 1 Ahora 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. o 13.4. ¿Qu´ es mejor? e Todo lo que se pueda hacer con modificadores puede hacerse tambi´n con fun- e ciones puras. En realidad, algunos lenguajes de programaci´n s´lo permiten o o funciones 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
  • 171. 13.5 Desarrollo de prototipos frente a planificaci´n o 145 los programas que usan modificadores. Sin embargo, a veces los modificadores son 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. ı o Este enfoque podr´ llamarse estilo funcional de programaci´n. ıa o 13.5. Desarrollo de prototipos frente a planifi- caci´n o En este cap´ ıtulo mostramos una aproximaci´n al desarrollo de programas a la o que llamamos desarrollo de prototipos. En cada caso, escribimos un esbozo basto (o prototipo) que realizaba el c´lculo b´sico y luego lo probamos sobre a a unos cuantos casos, corrigiendo los fallos tal como los encontr´bamos. a Aunque este enfoque puede ser efecitvo, puede conducirnos a c´digo que es o innecesariamente complicado, ya que trata con muchos casos especiales, y poco fiable, porque es dif´ saber si encontr´ todos los errores. ıcil o Una alternativa es el desarrollo planificado, en el que una comprensi´n del o problema en profundidad puede hacer la programaci´n mucho m´s f´cil. En este o a a caso, el enfoque es que un objeto Hora es en realidad ¡un n´mero de tres d´ u ıgitos en base 60! El componente segundo es la “columna de unidades”, el componente minuto es la “columna de las sesententas” y el componente hora es la “columna de las tresmilseiscentenas”. Cuando escribimos sumaHora e incremento, en realidad est´bamos haciendo a una suma en base 60, que es por lo que deb´ ıamos acarrear de una columna a la siguiente. Esta observaci´n sugiere otro enfoque para el problema. Podemos convertir un o objeto Hora en un simple n´mero y sacar provecho del hecho de que la m´quina u a sabe la aritm´tica necesaria. La siguiente funci´n convierte un objeto Hora en e o un entero: def convierteASegundos(t): minutos = t.horas * 60 + t.minutos segundos = minutos * 60 + t.segundos return segundos Ahora, s´lo necesitamos una forma de convertir un entero en un objeto Hora: o def haceHora(segundos): hora = Hora() hora.horas = segundos/3600
  • 172. 146 Clases y funciones segundos = segundos - hora.horas * 3600 hora.minutos = segundos/60 segundos = segundos - hora.minutos * 60 hora.segundos = segundos return hora Puede que tenga usted que pensar un poco para convencerse de que esta t´cni- e ca para convertir de una base a otra es correcta. Suponiendo que est´ usted a convencido, 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 a mostrar que es correcta (suponiendo, como es habitual, que las funciones a las que llama son correctas). Como ejercicio, reescriba incremento de la misma forma. 13.6. Generalizaci´n o De alg´n modo, convertir de base 60 a base 10 y de vuelta es m´s dif´ que u a ıcil simplemente manejarse con las horas. La conversi´n de base es m´s abstracta; o a nuestra intuici´n para tratar con las horas es mejor. o Pero si tenemos la comprensi´n para tratar las horas como n´meros en base 60, y o u hacer la inversi´n de escribir las funciones de conversi´n (convierteASegundos o o y haceHora), obtenemos un programa que es m´s corto, m´s f´cil de leer y a a a depurar y m´s fiable. a Tambi´n es m´s f´cil a˜adir funcionalidades m´s tarde. Por ejemplo, imagine e a a n a restar dos Horas para hallar el intervalo entre ellas. La aproximaci´n ingenua o ser´ implementar la resta con acarreo. Con el uso de las funciones de conversi´n ıa o ser´ m´s f´cil y con mayor probabilidad, correcto. a a a Ir´nicamente, a veces hacer un poblema m´s complejo (o m´s general) lo hace o a a m´s f´cil (porque hay menos casos especiales y menos oportunidades de error). a a 13.7. Algoritmos Cuando escribe una soluci´n general para una clase de problemas, en contraste o con una soluci´n espec´ o ıfica a un problema concreto, ha escrito un algoritmo.
  • 173. 13.8 Glosario 147 Mencionamos esta palabra antes pero no la definimos con precisi´n. No es f´cil o a de definir, as´ que probaremos un par de enfoques. ı Primero, piense en algo que no es un algoritmo. Cuando usted aprendi´ a mul- o tiplicar n´meros de una cifra, probablemente memoriz´ la tabla de multiplicar. u o En efecto, memoriz´ 100 soluciones espec´ o ıficas. Ese tipo de conocimiento no es algor´ıtmico. Pero si usted era “harag´n” probablemente hizo trampa aprendiendo algunos a trucos. 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 una soluci´n general para multiplicar cualquier n´mero de una cifra por 9. ¡Eso es o u un algoritmo! De forma similar, las t´cnicas que aprendi´ para la suma y la resta con aca- e o rreo y la divisi´n larga son todas algoritmos. Una de las caracter´ o ısticas de los algoritmos es que no requieren inteligencia para llevarse a cabo. Son procesos mec´nicos en los que cada paso sigue al anterior de acuerdo a un conjunto simple a de reglas. En nuestra opini´n, es un poco vergonzoso que los humanos pasen tanto tiempo o en 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 ıo lectual 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. Entender el 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 o de un algoritmo. 13.8. Glosario funci´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 ıa modificador: 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 ıa estilo funcional de programaci´n: Un estilo de programaci´n en el que la o o mayor´ de las funciones son puras. ıa desarrollo de prototipos: Una forma de desarrollar programas empezando con un prototipo y prob´ndolo y mejor´ndolo gradualmente. a a
  • 174. 148 Clases y funciones desarrollo 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
  • 175. Cap´ ıtulo 14 Clases y m´todos e 14.1. Caracter´ ısticas de la orientaci´n a objetos o Python es un lenguaje de programaci´n orientado a objetos, lo que signi- o fica que porporciona caracter´ ısticas que apoyan la programaci´n orientada o a objetos. No es f´cil definir la programaci´n orientada a objetos, pero ya hemos visto a o algunas 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. u Por ejemplo, la clase Hora definida en el Cap´ ıtulo 13 se corresponde con la forma en 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 a de un punto y un rect´ngulo. a Hasta ahora, no nos hemos aprovechado de las caracter´ ısticas que Python nos ofrece para dar soporte a la programaci´n orientada a objetos. Hablando estric- o tamente, estas caracter´ısticas no son necesarias. En su mayor´ proporcionan ıa, una sintaxis alternativa para cosas que ya hemos hecho, pero en muchos casos,
  • 176. 150 Clases y m´todos e la alternativa es m´s concisa y expresa con m´s precisi´n a la estructura del a a o programa. Por ejemplo, en el programa Hora no hay una conexi´n obvia entre la definici´n o o de la clase y las definiciones de las funciones que siguen. Observando bien, se hace patente que todas esas funciones toman al menos un objeto Hora como par´metro. a Esta observaci´n es la que motiva los m´todos. Ya hemos visto varios m´todos, o e e como keys y values, que se invocan sobre diccionarios. Cada m´todo est´ aso- e a ciado con una clase y est´ pensado para invocarse sobre instancias de esa clase. a Los 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. o En las pr´ximas secciones tomaremos las funciones de los cap´ o ıtulos anteriores y las transformaremos en m´todos. Esta transformaci´n es puramente mec´nica; e o a puede hacerla simplemente siguiendo una secuencia de pasos. Si se acostumbra a convertir de una forma a la otra ser´ capaz de elegir la mejor forma de hacer a lo que quiere. 14.2. imprimeHora En el Cap´ ıtulo 13, definimos una clase llamada Hora y escribimos una fuci´n o llamada imprimeHora, que deber´ ser parecida a esto: ıa class Hora: pass def 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)
  • 177. 14.3 Otro ejemplo 151 Para convertir imprimeHora en un m´todo, todo lo que necesitamos hacer es e mover la definici´n de la funci´n al interior de la definici´n de la clase. F´ o o o ıjese en c´mo cambia el sangrado. o class 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 e del punto y el nombre del m´todo aparece tras el punto. e El 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. a Por convenio, el primer par´metro de un m´todo se llama self. La raz´n de a e o esto 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 o que 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- o vocaci´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 a util. 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 escribir funciones m´s vers´tiles, y hace m´s f´cil mantener y reutilizar c´digo. a a a a o 14.3. Otro ejemplo Vamos a convertir incremento (de la Secci´n 13.3) en un m´todo. Para aho- o e rrar espacio, dejaremos a un lado los m´todos ya definidos, pero usted deber´ e ıa mantenerlos en su versi´n: o class Hora: #aqu´ van las definiciones anteriores de m´todos... ı e
  • 178. 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 + 1 La transformaci´n es puramente mec´nica; hemos llevado la definici´n del m´to- o a o e do al interior de la definici´n de la clase y hemos cambiado el nombre del primer o par´metro. a Ahora podemos invocar incremento como un m´todo. e horaActual.incremento(500) De nuevo, el objeto sobre el que invocamos el m´todo se asigna al primer par´me- e a tro, 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. e 14.4. Un ejemplo m´s complicado a La funci´n despues es ligeramente m´s complicada porque opera sobre dos o a objetos Hora, no s´lo sobre uno. S´lo podemos convertir uno de los par´metros o o a en self; el otro se queda como est´: a class 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
  • 179. 14.5 Argumentos opcionales 153 if self.minutos < hora2.minutos: return 0 if self.segundos > hora2.segundos: return 1 return 0 Invocamos este m´todo sobre un objeto y pasamos el otro como argumento: e if horaHecho.despues(horaActual): print "El pan estar´ hecho despu´s de empezar." a e Casi puede leer la invocaci´n como una mezcla de ingl´s y espa˜ol: “Si la hora- o e n hecho es depu´s de la hora-actual, entonces...” e 14.5. Argumentos opcionales Hemos visto funciones internas que toman un n´mero variable de argumentos. u Por 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 o para que haga lo mismo que string.find. Esta es la versi´n original de la Secci´n 7.7: o o def encuentra(cad, c): indice = 0 while indice < len(cad): if str[indice] == c: return indice indice = indice + 1 return -1 Esta es la versi´n aumentada y mejorada: o def encuentra(cad, c, comienzo=0): indice = comienzo while indice < len(cad): if str[indice] == c: return indice indice = indice + 1 return -1
  • 180. 154 Clases y m´todos e El tercer par´metro, comienzo, es opcional porque se proporciona un valor por a omisi´n, 0. Si invocamos encuentra s´lo con dos argumentos, utilizamos el valor o o por omisi´n y comenzamos por el principio de la cadena: o >>> encuentra("arriba", "r") 1 Si 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 o El m´todo de inicializaci´n es un m´todo especial que se invoca al crear un e o e objeto. El nombre de este m´todo es init (dos guiones bajos, seguidos de e init y dos guiones bajos m´s). Un m´todo de inicializaci´n para la clase Hora a e o es as´ ı: class Hora: def __init__(self, horas=0, minutos=0, segundos=0): self.horas = horas self.minutos = minutos self.segundos = segundos No hay conflicto entre el atributo self.horas y el par´metro horas. la notaci´n a o de punto especifica a qu´ variable nos referimos. e Cuando invocamos el constructor Hora, los argumentos que damos se pasan a init: >>> horaActual = Hora(9, 14, 30) >>> horaActual.imprimeHora() >>> 9:14:30
  • 181. 14.7 Revisi´n de los Puntos o 155 Como los par´metros son opcionales, podemos omitirlos: a >>> horaActual = Hora() >>> horaActual.imprimeHora() >>> 0:0:0 O dar s´lo el primer par´metro: o a >>> horaActual = Hora (9) >>> horaActual.imprimeHora() >>> 9:0:0 O los dos primeros par´metros: a >>> horaActual = Hora (9, 14) >>> horaActual.imprimeHora() >>> 9:14:0 Finalmente, podemos dar un subconjunto de los par´metros nombr´ndolos ex- a a plicitamente: >>> horaActual = Hora(segundos = 30, horas = 9) >>> horaActual.imprimeHora() >>> 9:0:30 14.7. Revisi´n de los Puntos o Vamos a reescribir la clase Punto de la Secci´n 12.1 con un estilo m´s orientado o a a 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 a nales; el valor por omisi´n de cada par´metro es 0. o a El siguiente m´todo, str , devuelve una representaci´n en forma de cadena e o de un objeto Punto. Si una clase ofrece un m´todo llamado str , se impone e al comportamiento por defecto de la funci´n interna str de Python. o
  • 182. 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. o 14.8. Sobrecarga de operadores Algunos lenguajes hacen posible cambiar la definici´n de los operadores internos o cuando se aplican a tipos definidos por el usuario. Esta caracter´ıstica se llama sobrecarga de operadores. Es especialmente util cuando definimos nuevos ´ tipos matem´ticos. a Por ejemplo, para suplantar al operador de suma + necesitamos proporcionar un m´todo llamado add : e class 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 a m´todo. El segundo par´metro se llama convenientemente otro para distinguirlo e a del mismo (self). Para sumar dos Puntos, creamos y devolvemos un nuevo Punto que contiene la suma de las coordenadas x y la suma de las coordenadas y. 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)
  • 183. 14.8 Sobrecarga de operadores 157 La expresi´n p1 + p2 equivale a p1. add (p2), pero es obviamente m´s ele- o a gante. Como ejercicio, a˜ada un m´todo sub (self, otro) que sobre- n e cargue el operador resta y pru´belo. e Hay varias formas de sobrecargar el comportamiento del operador multiplica- ci´n: definiendo un m´todo llamado mul , o rmul , o ambos. o e Si el operando a la izquierda de * es un Punto, Python invoca a mul , lo que presupone que el otro operando es tambi´n un Punto. Calcula el producto e interno de dos puntos, definido seg´n las reglas del ´lgebra lineal: u a def __mul__(self, otro): return self.x * otro.x + self.y * otro.y Si el operando a la izquierda de * es un tipo primitivo y el operando de la derecha es un Punto, Python invca a rmul , lo que realiza una multiplicaci´n o escalar: 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- u nadas originales. Si otro es un tipo que no se puede multiplicar por un n´mero u en coma flotante, entonces rmul causar´ un error. a Este ejemplo muestra ambos tipos de multiplicaci´n: o >>> p1 = Punto(3, 4) >>> p2 = Punto(5, 7) >>> print p1 * p2 43 >>> print 2 * p2 (10, 14) ¿Qu´ ocurre si intentamos evaluar p2 * 2? Como el primer par´metro es un e a Punto, 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 lo consigue porque un entero no tiene atributos: >>> print p2 * 2 AttributeError: ’int’ object has no attribute ’x’ Desgraciadamente, el mensaje de error es un poco opaco. Este ejemplo muestra algunas de las difucultades de la programaci´n orientada a objetos. A veces es o dif´ averiguar simplemente qu´ c´digo se est´ ejecutando. ıcil e o a
  • 184. 158 Clases y m´todos e Para ver un ejemplo m´s completo de sobrecarga de operadores, vaya al Ap´ndi- a e ce B. 14.9. Polimorfismo La mayor´ de los m´todos que hemos escrito funcionan s´lo para un tipo es- ıa e o pec´ıfico. Cuando usted crea un nuevo objeto, escribe m´todos que operan sobre e ese tipo. Pero hay ciertas operaciones que querr´ aplicar a muchos tipos, como las opera- a ciones aritm´ticas de las secciones anteriores. Si muchos tipos admiten el mismo e conjunto de operaciones, puede escribir funciones que trabajen sobre cualquiera de esos tipos. Por ejemplo, la operaci´n multisuma (com´n en ´lgebra lineal) toma tres o u a par´metros; multiplica los dos primeros y luego suma el tercero. Podemos escri- a birla en Python as´ ı: def multisuma (x, y, z): return x * y + z Este m´todo trabajar´ con cualquier valor de x e y que se pueda multiplicar y e a con cualquier valor de z que se pueda sumar al producto. Podemos invocarlo con valores num´ricos: e >>> multisuma (3, 2, 1) 7 O con Puntos: >>> p1 = Punto(3, 4) >>> p2 = Punto(5, 7) >>> print multisuma (2, p1, p2) (11, 15) >>> print multisuma (p1, p2, 1) 44 En el primer caso, el Punto se multiplica por un escalar y luego se suma a otro Punto. En el segundo caso, el producto interior produce un valor num´rico, e as´ que el tercer par´metro tambi´n debe ser un valor num´rico. ı a e e Una funci´n como ´sta que puede tomar par´metros con diferentes tipos se o e a llama polim´rfica. o Como un ejemplo m´s, observe el m´todo delDerechoYDelReves, que imprime a e dos veces una lista, hacia adelante y hacia atr´s: a
  • 185. 14.9 Polimorfismo 159 def 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 e antes de darle la vuelta. As´ este m´todo no modifica la lista que recibe como ı, e par´metro. a He 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. e Para determinar si una funci´n se puede aplicar a un nuevo tipo, aplicamos la o regla 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. o Las operaciones del m´todo incluyen copy, reverse y print. e copy trabaja sobre cualquier objeto, y ya hemos escrito un m´todo str para e los Puntos, as´ que todo lo que necesitamos es un m´todo reverse en la clase ı e Punto: def reverse(self): self.x , self.y = self.y, self.x Ahora 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 que una funci´n que hab´ escrito se puede aplicar a un tipo para el que nunca la o ıa hab´ planeado. ıa
  • 186. 160 Clases y m´todos e 14.10. Glosario lenguaje 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. e m´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. e multiplicaci´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. e polim´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
  • 187. Cap´ ıtulo 15 Conjuntos de objetos 15.1. Composici´n o Hasta ahora, ya ha visto varios ejemplos de composici´n. Uno de los primeros o ejemplos fue el uso de la llamada a un m´todo como parte de una expresi´n. e o Otro ejemplo es la estructura anidada de las sentencias; se puede escribir una sentencia 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 ıa sorprender que pueda crear listas de objetos. Tambi´n puede crear objetos que e contengan 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 Carta Si no est´ usted familiarizado con los naipes de juego comunes, puede ser un a buen momento para que consiga un mazo, si no este cap´ ıtulo puede que no tenga mucho sentido. Hay cincuenta y dos naipes en una baraja inglesa, cada uno de los cuales pertenece a un palo y tiene un valor; hay cuatro palos diferentes y trece valores. Los palos son Picas, Corazones, Diamantes, y Tr´boles (en el orden e descendente seg´n el bridge). Los valores son As, 2, 3, 4, 5, 6, 7, 8, 9, 10, Sota, u Reina, y Rey. Dependiendo del tipo de juego que se juegue, el valor del As puede ser mayor al Rey o inferior al 2.
  • 188. 162 Conjuntos de objetos Si queremos definir un nuevo objeto para representar un naipe, es obvio qu´ atri- e butos deber´ tener: valor y palo. Lo que no es tan obvio es el tipo que se debe ıa dar a los atributos. Una posibilidad es usar cadenas de caracteres que contengan palabras como "Picas" para los palos y "Reina" para los valores. Un problema de esta implementaci´n es que no ser´ f´cil comparar naipes para ver cu´l tiene o a a a mayor valor o palo. Una alternativa es usar n´meros enteros para codificar los valores y palos. Con u el t´rmino “codificar” no queremos significar lo que algunas personas pueden e pensar, acerca de cifrar o traducir a un c´digo secreto. Lo que un programador o entiende por “codificar” es “definir una correspondencia entre una secuencia de n´meros y los elementos que se desea representar”. Por ejemplo: u Picas → 3 Corazones → 2 Diamantes → 1 Tr´boles e → 0 Esta correspondencia tiene una caracter´ ıstica obvia: los palos corresponden a n´meros enteros en orden, o sea que podemos comparar los palos al comparar u los n´meros. La asociaci´n de los valores es bastante obvia; cada uno de los u o valores num´ricos se asocia con el entero correspondiente, y para las figuras: e Sota → 11 Reina → 12 Rey → 13 Estamos usando una notaci´n matem´tica para estas asociaciones por una o a raz´n: no son parte del programa Python. Son parte del dise˜o del progra- o n ma, pero nunca aparecen expl´ ıcitamente en el c´digo fuente. La definici´n de o o clase para el tipo Carta se parecer´ a: a class Carta: def __init__(self, palo=0, valor=0): self.palo = palo self.valor = valor Como acostumbramos, proporcionaremos un m´todo de inicializaci´n que toma e o un par´metro opcional para cada atributo. a Para crear un objeto que representa el 3 de Tr´boles, usaremos la instrucci´n: e o tresDeTreboles = Carta(0, 3) El primer argumento, 0, representa el palo de Tr´boles. e
  • 189. 15.3 Atributos de clase y el m´todo e str 163 15.3. Atributos de clase y el m´todo e str Para poder imprimir los objetos Carta de una manera f´cil de leer para las a personas, vamos a establecer una correspondencia entre los c´digos enteros y o las palabras. Una manera natural de hacer esto es con listas de cadenas de caracteres. Asignaremos estas listas dentro de atributos de clase al principio de la definici´n de clase: o class 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 e desde cualquiera de los m´todos de la clase. e Dentro de str , podemos usar listaDePalos y listaDeValores para aso- ciar los valores num´ricos de palo y valor con cadenas de caracteres. Por e ejemplo, la expresi´n self.listaDePalos[self.palo] significa “usa el atribu- o to palo del objeto self como un ´ ındice dentro del atributo de clase denominado listaDePalos, y selecciona la cadena apropiada”. El motivo del ‘‘nada" en el primer elemento de listaDeValores es para relleno del 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 este primer elemento. Podr´ ıamos haber comenzado en 0 como es usual, pero es menos confuso 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 carta1 Sota de Diamantes Los atributos de clase como listaDePalos son compartidos por todos los ob- jetos de tipo Carta. La ventaja de esto es que podemos usar cualquier objeto Carta para acceder a los atributos de clase:
  • 190. 164 Conjuntos de objetos >>> carta2 = Carta(1, 3) >>> print carta2 3 de Diamantes >>> print carta2.listaDePalos[1] Diamantes La desventaja es que si modificamos un atributo de clase, afectaremos a cada instancia de la clase. Por ejemplo, si decidimos que “Sota de Diamantes” en realidad deber´ llamarse “Sota de Ballenas Bailarinas”, podr´ ıa ıamos hacer lo siguiente: >>> carta1.listaDePalos[1] = "Ballenas Bailarinas" >>> print carta1 Sota de Ballenas Bailarinas El problema es que todos los Diamantes se transformar´n en Ballenas Bailarinas: a >>> print carta2 3 de Ballenas Bailarinas En general no es una buena idea modificar los atributos de clase. 15.4. Comparaci´n de naipes o Para los tipos primitivos, existen operadores condicionales (< , >, ==, etc.) que comparan valores y determinan cuando uno es mayor, menor, o igual a otro. Para los tipos definidos por el usuario, podemos sustituir el comportamiento de los operadores internos si proporcionamos un m´todo llamado cmp . Por e convenci´n, cmp toma dos par´metros, self y otro, y retorna 1 si el primer o a objeto 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 a comparar dos elementos cualesquiera y decir cu´l es el mayor. Por ejemplo, los a n´meros enteros y los n´meros en coma flotante tienen un orden completo. Algu- u u nos conjuntos no tienen orden, o sea, que no existe ninguna manera significativa de decir que un elemento es mayor a otro. Por ejemplo, las frutas no tienen orden, lo que explica por qu´ no se pueden comparar peras con manzanas. e El conjunto de los naipes tiene un orden parcial, lo que significa que algunas veces se pueden comparar los naipes, y otras veces no. Por ejemplo, usted sabe que el 3 de Tr´boles es mayor que el 2 de Tr´boles y el 3 de Diamantes es e e mayor que el 3 de Tr´boles. Pero, ¿cu´l es mejor?, ¿el 3 de Tr´boles o el 2 de e a e Diamantes?. Uno tiene mayor valor, pero el otro tiene mayor palo.
  • 191. 15.5 Mazos de naipes 165 A los fines de hacer que los naipes sean comparables, se debe decidir qu´ es m´s e a importante: valor o palo. Para no mentir, la selecci´n es arbitraria. Como algo o hay que elegir, diremos que el palo es m´s importante, porque un mazo nuevo a viene ordenado con todos los Tr´boles primero, luego con todos los Diamantes, e y 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 0 En 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 naipes Ahora que ya tenemos los objetos para representar las Cartas, el pr´ximo paso o l´gico es definir una clase para representar un Mazo. Por supuesto, un mazo o est´ compuesto de naipes, as´ que cada objeto Mazo contendr´ una lista de a ı a naipes como atributo. A continuaci´n se muestra una definici´n para la clase Mazo. El m´todo de o o e inicializaci´n crea el atributo cartas y genera el conjunto est´ndar de cincuenta o a y 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 a exterior 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
  • 192. 166 Conjuntos de objetos itera trece veces, la cantidad total de veces que se ejecuta el cuerpo interior es cincuenta y dos (trece por cuatro). Cada iteraci´n crea una nueva instancia de o Carta 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. e 15.6. Impresi´n del mazo de naipes o Como es usual, cuando definimos un nuevo tipo de objeto queremos un m´todo e que imprima el contenido del objeto. Para imprimir un Mazo, recorremos la lista e imprimimos cada Carta: class Mazo: ... def muestraMazo(self): for carta in self.cartas: print carta Desde ahora en adelante, los puntos suspensivos (...) indicar´n que hemos a omitido los otros m´todos en la clase. e En 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 a lugar 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 o pueden 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 o cadena de caracteres de un Mazo. Para darle un toque especial, acomoda los naipes en una cascada, de tal manera que cada naipe est´ sangrado un espacio a m´s que el precedente. a class Mazo: ... def __str__(self): s = "" for i in range(len(self.cartas)): s = s + " "*i + str(self.cartas[i]) + "n" return s Este ejemplo demuestra varias caracter´ ısticas. Primero, en lugar de recorrer self.cartas y asignar cada naipe a una variable, usamos i como variable de bucle e ´ ındice de la lista de naipes. Segundo, utilizamos el operador de multiplicaci´n de cadenas de caracteres para o sangrar cada naipe un espacio m´s que el anterior. La expresi´n *i prooprciona a o una cantidad de espacios igual al valor actual de i.
  • 193. 15.7 Barajar el mazo 167 Tercero, en lugar de usar la instrucci´n print para imprimir los naipes, utiliza- o mos la funci´n str. El pasar un objeto como argumento a str es equivalente a o invocar el m´todo str sobre dicho objeto. e Finalmente, usamos la variable s como acumulador. Inicialmente, s es una cadena de caracteres vac´ En cada pasada a trav´s del bucle, se genera una ıa. e nueva cadena de caracteres que se concatena con el viejo valor de s para obtener el nuevo valor. Cuando el bucle termina, s contiene la representaci´n completa o en formato de cadena de caracteres del Mazo, la cual se ve como a continuaci´no se presenta: >>> mazo = Mazo() >>> print mazo As 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 Diamantes Y as´ sucesivamente. A´n cuando los resultados aparecen en 52 renglones, se ı u trata de s´lo una unica larga cadena de caracteres que contiene los saltos de o ´ l´ ınea. 15.7. Barajar el mazo Si un mazo est´ perfectamente barajado, cualquier naipe tiene la misma proba- a bilidad de aparecer en cualquier posici´n del mazo, y cualquier lugar en el mazo o tiene la misma probabilidad de contener cualquier naipe. Para mezclar el mazo, utilizaremos la funci´n randrange del m´dulo random. Es- o o ta funci´n toma dos enteros como argumentos a y b, y elige un n´mero entero en o u
  • 194. 168 Conjuntos de objetos forma 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 argumento y de esa manera tendremos garantizado un ´ ındice legal dentro de la lista. Por ejemplo, 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 intercambiar cada una con otra elegida al azar. Es posible que el naipe se intercambie consigo mismo, pero no es un problema. De hecho, si eliminamos esa posibilidad, el orden de los naipes no ser´ completamente al azar: a class 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, obtenemos la longitud real de la lista y la almacenamos en nCartas. Para cada naipe del mazo, seleccionamos un naipe al azar entre aquellos que no han sido intercambiados a´n. Luego intercambiamos el naipe actual (i) con el u naipe seleccionado (j). Para intercambiar los naipes usaremos la asignaci´n de o tuplas, como se describe en la Secci´n 9.2: o self.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. o 15.8. Eliminaci´n y reparto de los naipes o Otro 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 a en el mazo, y falso (0) si no estaba:
  • 195. 15.9 Glosario 169 class Mazo: ... def eliminaCarta(self, carta): if carta in self.cartas: self.cartas.remove(carta) return 1 else: return 0 El operador in retorna verdadero si el primer operando est´ en el segundo, el a cual debe ser una lista o tupla. Si el primer operando es un objeto, Python usa el m´todo cmp del objeto para determinar la igualdad entre los elementos de e la lista. Como el cmp en la clase Carta verifica la igualdad en profundidad, el m´todo eliminaCarta tambi´n verifica igualdad en profundidad. e e Para repartir los naipes, queremos eliminar y devolver el naipe que ocupa la posici´n superior en el mazo. El m´todo pop de las listas proporciona una manera o e conveniente 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 o estaVacio, la cual devuelve verdadero si el mazo no contiene ning´n naipe: u class Deck: ... def estaVacio(self): return (len(self.cartas) == 0) 15.9. Glosario codificar: 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.
  • 196. 170 Conjuntos de objetos acumulador: 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
  • 197. Cap´ ıtulo 16 Herencia 16.1. Herencia La caracter´ ıstica de un lenguaje que m´s se asocia con la programaci´n orientada a o a objetos es la herencia. La herencia es la capacidad de definir una nueva clase que es una versi´n modificada de otra ya existente. o La principal ventaja de esta caracter´ıstica es que se pueden agregar nuevos m´to- e dos a una clase sin modificar la clase existente. Se denomina “herencia” porque la nueva clase hereda todos los m´todos de la clase existente. Si extendemos e esta mat´fora, a la clase existente a veces se la denomina clase padre. La nueva e clase puede denominarse clase hija, o tambi´n “subclase”. e La herencia es una caracter´ıstica poderosa. Ciertos programas que ser´ compli- ıan cados 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 o adaptar 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 a Por 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 o su definici´n. El c´digo relevante puede estar diseminado por varios m´dulos. o o o Adem´s, muchas de las cosas que se hacen mediante el uso de la herencia, se pue- a den lograr de forma igualmente (incluso m´s) elegante sin ella. Si la estructura a general del problema no nos gu´ hacia la herencia, dicho estilo de programaci´n ıa o puede hacer m´s mal que bien. a
  • 198. 172 Herencia En este cap´ ıtulo demostraremos el uso de la herencia como parte de un programa que juega a las cartas a la “Mona”. Una de nuestras metas ser´ que el c´digo a o que escribamos se pueda reutilizar para implementar otros juegos de naipes. 16.2. Una mano de cartas Para 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 a conjunto de naipes, y ambos requieren de operaciones tales como agregar y eliminar una carta. Adem´s, necesitaremos la capacidad de mezclar tanto un a mazo como una mano de cartas. Una mano es diferente de un mazo en ciertos aspectos. Seg´n el juego al que se u est´ jugando, podemos querer realizar ciertas operaciones sobre una mano que e no tienen sentido sobre un mazo. Por ejemplo, en el p´ker queremos clasificar o una mano (straight (consecutiva), flush (de un solo palo), etc.) y compararla con otra. 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, o entonces tendr´ todos los m´todos de Mazo y le podremos agregar otros m´todos a e e nuevos. En la definici´n de clase, el nombre de la clase padre aparece entre par´ntesis: o e class Mano(Mazo): pass Esta 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 y cartas. La cadena de caracteres nombre identifica a esta mano, probablemente mediante el nombre del jugador que la sostiene. El nombre es un par´metro a opcional 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 = nombre Casi 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 o de Mazo. Pero deberemos escribir agregaCarta:
  • 199. 16.3 El reparto de los naipes 173 class Mano(Mazo): ... def agregaCarta(self,carta) : self.cartas.append(carta) De nuevo, los puntos suspensivos indican que hemos omitido los otrs m´todos. e El m´todo de lista append agrega la nueva carta al final de la lista de cartas. e 16.3. El reparto de los naipes Ahora que ya tenemos la clase Mano, queremos repartir las cartas del Mazo en manos. No es claramente obvio si este m´todo debe ir en la clase Mano o en la e clase Mazo, pero como opera sobre un mazo unico y (posiblemente) sobre varias ´ manos, es m´s natural ponerlo en el Mazo. a repartir debe ser bastante general, pues los diferentes juegos tienen distintos requerimentos. Puede que necesitemos repartir todo el mazo de una vez, o que agreguemos una carta a cada mano. repartir toma dos par´metros, una lista (o tupla) de manos y la cantidad total a de naipes a repartir. Si no hay suficientes cartas en el mazo, el m´todo reparte e todas 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 mano El segundo par´metro, nCartas es opcional; el valor por omisi´n es un n´mero a o u muy grande, lo cual es lo mismo que decir que se repartir´n todos los naipes del a mazo. La variable de bucle i va desde 0 hasta nCartas-1. A cada paso a trav´s del e bucle, se elimina una carta del mazo mediante el m´todo de lista pop, que quita e y 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 manos en la lista, la expresi´n i % nManos salta hacia el comienzo de la lista (el ´ o ındice es 0).
  • 200. 174 Herencia 16.4. Mostremos la mano Para mostrar el contenido de una mano, podemos sacar partido de la existencia de 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 mano La mano de hugo contiene 2 de Picas 3 de Picas 4 de Picas As de Corazones 9 de Tr´boles e No es una gran mano, pero tiene lo necesario como para disponer de una escalera de color. Aunque es conveniente usar la herencia de los m´todos existentes, existe infor- e maci´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 e la 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 mano est´ 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 o como cadena de caracteres del Mazo, que se obtiene llamando al m´todo str e de la clase Mazo sobre la instancia self. Puede parecer extra˜o que enviemos a self, que se refiere a la Mano actual, n como argumento de un m´todo de la clase Mazo, hasta que nos damos cuenta e de que una Mano es un tipo de Mazo. Los objetos Mano pueden hacer cualquier
  • 201. 16.5 La clase JuegoDeCartas 175 cosa que pueda hacer un objeto Mazo, y por ello es legal que pasemos una Mano a un m´todo de Mazo. e En general, siempre es legal usar una instancia de una subclase en el lugar de una instancia de una clase padre. 16.5. La clase JuegoDeCartas La clase JuegoDeCartas asume la responsabilidad sobre algunas obligaciones b´sicas comunes a todos los juegos, tales como la creaci´n del mazo y la mezcla a o de 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 o actividad computacional significativa, m´s all´ de la inicializaci´n de atributos. a a o Para implementar juegos espec´ ıficos, debemos heredar de JuegoDeCartas y agregar las caracter´ ısticas del nuevo juego. Como ejemplo, escribiremos una simulaci´n para La Mona. o La 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, a el 4 de Tr´boles se empareja con el 4 de Picas porque ambos palos son negros. e La Sota de Corazones se empareja con la Sota de Diamantes porque ambos son rojos. Para iniciar el juego, se elimina la Reina de Tr´boles del mazo, de manera que e la Reina de Picas no tiene con qui´n emparejarse. Las cincuenta y una cartas e restantes 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, a cada jugador toma una carta (sin mirarla) del vecino m´s cercano de la izquierda a que a´n tiene cartas. Si la carta elegida concuerda con una de la mano del u jugador, 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 lo que queda s´lo la Reina de Picas en la mano del perdedor. o En nuestra simulaci´n inform´tica del juego, la computadora juega todas las o a manos. Desafortunadamente, se pierden algunos de los matices del juego real. En una partida real, el jugador que tiene la Mona realiza ciertos esfuerzos para
  • 202. 176 Herencia que su vecino la tome, por ejemplo mostr´ndola prominentemente o al contrario, a errando al intentar mostrarla abiertamente, o incluso puede fallar al tratar de errar en su intento de mostrarla prominentemente. La computadora simplemente toma una carta al azar de su vecino. 16.6. La clase ManoDeLaMona Una mano para jugar a La Mona requiere ciertas capacidades que est´n m´s a a all´ de las que posee una Mano. Definiremos una nueva clase ManoDeLaMona, a que hereda de Mano y nos proporciona un m´todo adicional denominado e eliminaCoincidencias: 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 cant Comenzamos por hacer una copia de la lista de las cartas, de tal manera que podamos recorrer la copia mientras vamos quitando cartas de la lista original. Como self.cartas se modifica en el bucle, no vamos a querer usarla para controlar el recorrido. ¡Python puede quedar realmente confundido si se recorre una lista que est´ cambiando! a Para cada carta de la mano, averiguamos cu´l es la carta que concordar´ con a a ella y la buscamos. La carta que concuerda tiene el mismo valor y el otro palo del mismo color. La expresi´n 3 - carta.palo transforma un Tr´bol (palo 0) o e en una Pica (palo 3) y un Diamante (palo 1) en un Coraz´n (palo 2). Verifique o por su cuenta que las operaciones opuestas tambi´n funcionan. Si la carta que e concuerda est´ en la mano, ambas se eliminan. a El siguiente ejemplo demuestra el uso de eliminaCoincidencias: >>> juego = JuegoDeCartas() >>> mano = ManoDeLaMona("hugo") >>> juego.mazo.repartir([mano], 13)
  • 203. 16.7 La clase JuegoDeLaMona 177 >>> print mano La mano de hugo contiene As 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 e Mano hugo: 8 de Picas con 8 de Tr´boles e Mano hugo: 10 de Diamantes con 10 de Corazones Debe usted notar que no existe un m´todo e init para la clase ManoDeLaMona. Lo heredamos de Mano. 16.7. La clase JuegoDeLaMona Ahora 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 e toma una lista de jugadores como par´metro. a Como el m´todo init se hereda de JuegoDeCartas, el nuevo objeto e JuegoDeLaMona 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))
  • 204. 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- e parados. eliminaTodasLasCoincidencias recorre la lista de manos y llama a eliminaCoincidencias 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 cada mano y devuelve el total. Cuando la cantidad total de coincidencias alcanza a las veinticinco significa que se han eliminado cincuenta cartas de las manos, lo que es lo mismo que decir que s´lo queda una carta y el juego ha terminado. o La variable turno recuerda el turno de cu´l jugador se est´ jugando. Comienza a a en cero y se incrementa en uno cada vez; cuando alcanza el valor cantManos, el operador de m´dulo lo hace volver a cero. o
  • 205. 16.7 La clase JuegoDeLaMona 179 El m´todo jugarUnTurno toma un par´metro que indica de qui´n es el turno. e a e El valor de retorno es la cantidad de concordancias que se han realizado durante ese 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 cant Si 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´nu tiene 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 o del siguiente jugador sea al azar. El m´todo encuentraVecino comienza con el jugador que est´ inmediatamante e a a la izquierda y contin´a alrededor del c´ u ırculo hasta que encuentra un jugador que a´n tiene cartas. u class 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 vecino Si por cualquier motivo encuentraVecino llegara a dar la vuelta completa al c´ ırculo sin encontrar cartas, devolver´ None y eso causar´ un error en alguna ıa ıa otra parte del programa. Afortunadamante podemos probar que eso no va a suceder nunca (siempre y cuando se detecte correctamente el final del juego). ´ Hemos omitido el m´todo muestraManos. Ese puede escribirlo usted mismo. e La 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
  • 206. 180 Herencia jugadores. Con este mazo m´s peque˜o, el juego termina tras siete coincidencias, a n en lugar de veinticinco. >>> import cartas >>> juego = cartas.JuegoDeLaMona() >>> juego.jugar(["Allen","Jeff","Chris"]) ----- Se han repartido las cartas. Mano Allen contiene Rey de Corazones Sota de Tr´boles e Reina de Picas Rey de Picas 10 de Diamantes Mano Jeff contiene Reina de Corazones Sota de Picas Sota de Corazones Rey de Diamantes Reina de Diamantes Mano Chris contiene Sota de Diamantes Rey de Tr´boles e 10 de Picas 10 de Corazones 10 de Tr´boles e Mano Jeff: Reina de Corazones con Reina de Diamantes Mano Chris: 10 de Picas con 10 de Tr´boles e ----- Se eliminaron las coincidencias, el juego comienza. Mano Allen contiene Rey de Corazones Sota de Tr´boles e Reina de Picas Rey de Picas 10 de Diamantes Mano Jeff contiene Sota de Picas Sota de Corazones Rey de Diamantes
  • 207. 16.8 Glosario 181 Mano Chris contiene Sota de Diamantes Rey de Tr´boles e 10 de Corazones Mano Allen: eligi´ Rey de Diamantes o Mano Allen: Rey de Corazones con Rey de Diamantes Mano Jeff: eligi´ 10 de Corazones o Mano Chris: eligi´ Sota de Tr´boles o e Mano Allen: eligi´ Sota de Corazones o Mano Jeff: eligi´ Sota de Diamantes o Mano Chris: eligi´ Reina de Picas o Mano Allen: eligi´ Sota de Diamantes o Mano Allen: Sota de Corazones con Sota de Diamantes Mano Jeff: eligi´ Rey de Tr´boles o e Mano Chris: eligi´ Rey de Picas o Mano Allen: eligi´ 10 de Corazones o Mano Allen: 10 de Diamantes con 10 de Corazones Mano Jeff: eligi´ Reina de Picas o Mano Chris: eligi´ Sota de Picas o Mano Chris: Sota de Tr´boles con Sota de Picas e Mano Jeff: eligi´ Rey de Picas o Mano Jeff: Rey de Tr´boles con Rey de Picas e ----- El juego termin´. o La mano de Allen est´ vac´a. a ı La mano de Jeff contiene Reina de Picas La mano de Chris est´ vac´a. a ı As´ que Jeff es quien perdi´. ı o 16.8. Glosario herencia: 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.
  • 208. 182 Herencia clase hija: Una nueva clase creada heredando de una clase existente; tambi´n e se la llama “subclase”.
  • 209. Cap´ ıtulo 17 Listas enlazadas 17.1. Referencias incrustadas Hemos visto ejemplos de atributos que hacen referencia a otros objetos, a los que llamamos referencias incrustadas (v´ase la Secci´n 12.8). Una estrutura e o de datos com´n, la lista enlazada, saca partido de esta caracter´ u ıstica. Las listas enlazadas se componen de nodos, donde cada nodo contiene una referencia al pr´ximo nodo de la lista. Adem´s, cada nodo contiene una unidad o a de datos llamada carga Podemos 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. e 17.2. La clase Nodo Como es habitual cuando se escribe una clase, comenzaremos con los m´todos e de inicializaci´n y str , para poder comprobar el mecanismo b´sico de crear o a y mostrar el nuevo tipo:
  • 210. 184 Listas enlazadas class 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 o les. 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 o en una lista. Para comprobar la implementaci´n en este punto, podemos crear un Nodo e o imprimirlo: >>> nodo = Nodo("prueba") >>> print nodo prueba Para hacerlo m´s interesante, necesitaremos una lista que contenga m´s de un a a nodo: >>> 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 u todav´ 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 None Para enlazar los nodos, debemos hacer que el primer nodo haga referencia al segundo, y que ´ste haga referencia al tercero: e >>> nodo1.siguiente = nodo2 >>> nodo2.siguiente = nodo3 La referencia del tercer nodo ser´ None, que indica que es el final de la lista. a Ahora el diagrama de estados tendr´ el siguiente aspecto: a
  • 211. 17.3 Listas como colecciones 185 node1 node2 node3 cargo 1 cargo 2 cargo 3 next next next None Ahora ya sabe c´mo crear nodos y enlazarlos en listas. Lo que podr´ estar o ıa menos claro es por qu´. e 17.3. Listas como colecciones Las listas son utiles porque aportan un modo de ensamblar m´ltiples objetos u dentro de una unica entidad, a veces llamada colecci´n. En el ejemplo, el primer ´ o nodo 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 o nodo. Por ejemplo, la funci´n imprimeLista toma como argumento un nodo o simple. Empezando con la cabeza de la lista, imprime cada nodo hasta que llega al final. def imprimeLista(nodo): while nodo: print nodo, nodo = nodo.siguiente print Para llamar a este m´todo, pasamos una referencia al primer nodo: e >>> imprimeLista(nodo1) 1 2 3 Dentro de imprimeLista tenemos una referencia al primer nodo de la lista, pero no hay una variable que haga referencia a los otros nodos. Tendremos que usar el valor de siguiente de cada nodo para acceder al siguiente nodo. Para recorrer una lista enlazada es habitual usar la variable de un bucle como nodo para hacer referencia sucesivamente a cada uno de los nodos. Este diagrama muestra el valor de lista y los valores que va tomando nodo:
  • 212. 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 recursividad Es natural expresar muchas operaciones con listas por medio de m´todos re- e cursivos. El siguiente ejemplo es un algoritmo recursivo para imprimir una lista hacia 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 manera de imprimir una lista del rev´s. Pero si suponemos que la llamada recursiva e funciona —el acto de fe— entonces podemos estar convencidos de que este algoritmo funciona. Todo lo que necesitamos es un caso b´sico y una forma de demostrar que para a cualquier lista podemos obtener el caso b´sico. Dada la definici´n recursiva de a o una 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,
  • 213. 17.5 Listas infinitas 187 La primera l´ınea maneja el caso inicial no haciendo nada. Las dos siguientes l´ ıneas dividen la lista en cabeza y cola. Las dos ultimas l´ ´ ıneas imprimen la lista. La coma que hay al final de la ultima l´ ´ ınea evita que Python salte de l´ ınea despu´s de imprimir un nodo. e Llamamos este m´todo tal como antes invocamos a imprimeLista: e >>> imprimeAlReves(nodo1) 3 2 1 El resultado es una lista invertida. Es posible que se pregunte por qu´ imprimeLista e imprimeAlReves son fun- e ciones y no m´todos de la clase Nodo. La raz´n es que queremos usar None para e o representar la lista vac´ y no es legal llamar un m´todo sobre None. Esta limi- ıa e taci´n resulta algo inc´moda a la hora de escribir c´digo para manipular listas o o o con 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 a listas har´n que el m´todo no funcione. a e 17.5. Listas infinitas No hay nada que impida a un nodo hacer referencia a un nodo anterior de la lista, incluido ´l mismo. Por ejemplo, esta figura muestra una lista con dos e nodos, uno de los cuales apunta a s´ mismo: ı list cargo 1 cargo 2 next next Si llamamos a imprimeLista sobre esta lista, entrar´ en un bucle infinito. Si lla- a mamos a imprimeAlReves, lo har´ de forma infinitamente recursiva. Eeste tipo a de comportamiento da lugar a que sea muy dif´ trabajar con listas infinitas. ıcil Sin embargo, en ocasiones resultan utiles. Por ejemplo, podr´ ´ ıamos representar un n´mero como una lista de d´ u ıgitos y usar una lista infinita para representar una fracci´n repetida. o
  • 214. 188 Listas enlazadas A pesar de todo, es un problema que no podamos probar que imprimeLista e imprimeAlReves puedan terminar correctamente. Lo mejor que podemos hacer es afirmar que “si la lista no contiene bucles, estos m´todos podr´n terminar”. e a Este tipo de afirmaciones se conocen como condiciones previas. Imponen una restricci´n sobre uno de los par´metros y describen el comportamiento del o a m´todo si la restricci´n se satisface. Veremos m´s ejemplos m´s adelante. e o a a 17.6. Teorema fundamental de la ambig¨ edad u Una parte de imprimeAlReves podr´ habernos sorprendido: ıa cabeza = lista cola = lista.siguiente Despu´s de la primera asignaci´n, la cabeza y la cola tienen el mismo tipo y e o el mismo valor. Asi que, ¿para qu´ hemos creado un nueva variable? e La raz´n es que las dos variables desempe˜an papeles diferentes. Pensamos en o n la cabeza como una referencia al primer nodo de la lista. Estos “papeles” no forman parte del programa, sino que est´n en la mente del programador. a En general no podemos decir con s´lo mirar un programa qu´ papel desem- o e pe˜ar´ un variable. Esta ambig¨edad puede ser util, pero tambi´n puede difi- n a u ´ e cultar la lectura del programa. A menudo usaremos nombres para las variables como nodo y lista para explicar c´mo queremos usar una variable y a veces o creamos variables adicionales para eliminar ambig¨edades. u Podr´ıamos haber escrito imprimeAlReves sin cabeza ni cola, que lo har´ mas ıa conciso, 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 sus argumentos como una colecci´n y print los trata como a un s´lo objeto. o o El teorema fundamental de la ambig¨ edad indica que la ambig¨edad que u u es 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.
  • 215. 17.7 Modificar listas 189 17.7. Modificar listas Hay dos formas de modificar una lista enlazada. Obviamente, podemos cambiar la carga de uno de los nodos, pero las operaciones m´s interesantes son las que a a˜aden, quitan o reordenan los nodos. n Como ejemplo, escribamos un m´todo que quite el segundo nodo de la lista y e devuelva 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 segundo De nuevo, estamos usando variables temporales para hacer m´s legible el c´digo. a o As´ es como usamos este m´todo: ı e >>> imprimeLista(nodo1) 1 2 3 >>> eliminado = elimnaSegundo(nodo1) >>> imprimeLista(eliminado) 2 >>> imprimeLista(nodo1) 1 3 El 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 ıa argumento? ¿Hay una condici´n previa para este m´todo? Si es as´ ser´ algo o e ı, ıa razonable establecer un m´todo para manejar una violaci´n de la condici´n e o o previa.
  • 216. 190 Listas enlazadas 17.8. Envoltorios y ayudantes A menudo es util dividir una operaci´n de listas en dos m´todos. Por ejemplo, ´ o e para imprimir una lista invertida en el formato convencional [3, 2, 1] pode- mos usar el m´todo imprimeAlReves para imprimir 3, 2, pero necesitaremos e un m´todo aparte para imprimir los corchetes y el primer nodo. Llam´moslo e e imprimeAlRevesBonito: 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 e funcionan con casos especiales como una lista vac´ o un singleton. ıa Cuando usamos este m´todo en alg´n otro lugar del programa, llamamos direc- e u tamente a imprimeAlRevesBonito, y ´ste llama a imprimeAlReves en nuestro e lugar. En cierto modo, imprimeAlRevesBonito act´a como un envoltorio, y u utiliza a imprimeAlReves como su ayudante. 17.9. La clase ListaEnlazada Existen ciertos problemas delicados con la forma en que se implementaron las listas. Inviertiendo el orden de causa y efecto, propondremos primero una im- plementaci´n alternativa y explicaremos luego los problemas que ´sta resuelve. o e En 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 primer nodo. Los objetos ListaEnlazada sirven de asas para la manipulaci´n de las o listas de los objetos de tipo Nodo: class ListaEnlazada : def __init__(self) : self.longitud = 0 self.cabeza = None Una ventaja de la clase ListaEnlazada es que suministra un marco natural para poner funciones-envoltorio como imprimeAlRevesBonito, que podemos convertir en un m´todo de la clase ListaEnlazada: e
  • 217. 17.10 Invariantes 191 class 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. a Ahora hay dos m´todos llamados imprimeAlReves: uno en la clase Nodo (el e ayudante), y otro en la clase ListaEnlazada (el envoltorio). Cuando el envolto- rio llama a self.cabeza.imprimeAlReves, est´ llamando al ayudante, ya que a self.cabeza es un objeto de tipo Nodo. Otra ventaja de la clase ListaEnlazada es que facilita la forma de a˜adir o qui- n tar el primer elemento de una lista. Por ejemplo, agregaPrimero es un m´todo e para ListaEnlazada; toma un elemento de la carga como argumento y lo coloca en 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 + 1 Como suele ser usual, deber´ ıamos comprobar ´ste c´digo para ver si maneja e o casos especiales. Por ejemplo, ¿qu´ ocurrir´ si la lista est´ unicialmente vac´ e ıa a ıa? 17.10. Invariantes Algunas listas est´n “bien construidas”; otras no. Por ejemplo, si una lista con- a tiene un bucle, provocar´ que nuestros m´todos se cuelguen, as´ que podr´ a e ı ıamos exigir que las listas no contengan bucles. Otro requisito es que el valor de el valor
  • 218. 192 Listas enlazadas de longitud en el objeto de tipo ListaEnlazada deber´ ser igual al n´mero ıa u verdadero 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 ıan para objetos es una pr´ctica util de la programaci´n porque hace m´s f´cil de- a ´ o a a mostrar la idoneidad del c´digo, comprobar la integridad de las estructuras de o datos y la detecci´n de errores. o Una cosa que a veces confunde respecto a los invariantes es que en ocasiones son violados. Por ejemplo, en medio de agregaPrimero, despu´s de a˜adir el nodo e n paro antes de incrementar la longitud, se viola el invariante. Se acepta este tipo de violaci´n; de hecho, a menudo es imposible modificar un objeto sin violar un o invariante durante al menos un corto espacio de tiempo. Normalmente, se exige que todo m´todo que viole un invariante debe restablecerlo. e Si hay alg´n tramo significativo de c´digo en el que el invariante se ve violado, u o es importante que los comentarios lo dejen claro, de modo que no se realicen operaciones que dependan del invariante. 17.11. Glosario referencia 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.
  • 219. 17.11 Glosario 193 envoltorio: 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 o invariante: Afirmaci´n que deber´ ser cierta para un objeto en todo momento o ıa (excepto tal vez cuando se est´ modificando el objeto). a
  • 221. Cap´ ıtulo 18 Pilas 18.1. Tipos abstractos de datos Los tipos de datos vistos hasta ahora han sido todos concretos, en el sentido que se ha especificado su implementaci´n completamente. Por ejemplo, la clase o Carta representa un naipe por medio de dos enteros. Como dijimos, esa no es la 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 a la 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
  • 222. 196 Pilas Cuando hablamos de TADs a menudo se hace la distinci´n entre el c´digo que o o usa el TAD, el c´digo cliente, y el c´digo que implementa el TAD, el c´digo o o o proveedor. 18.2. El TAD Pila En este cap´ıtulo se presentar´ un TAD com´n, la pila. Una pila es una colecci´n, a u o lo que significa que es una estructura de datos que contiene elementos m´ltiples. u Otras 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 e operaciones1 : init : Inicializar una pila nueva y vac´ ıa. push: A˜adir un elemento a la pila. n pop: Extraer un elemento de la pila. El elemento devuelto siempre es el ultimo ´ que se a˜adi´. n o isEmpty: 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 hon Las operaciones sobre listas que posee Python son parecidas a las operaciones que definen a una pila. La interfaz no es exactamente la que deber´ de ser, pero ıa se pueden desarrollar programas para convertir el TAD Pila a las operaciones predefinidas. A este programa se lo llama implementaci´n del TAD Pila. En general, una o implementaci´n es un conjunto de m´todos que satisfacen los prerrequisitos o e sint´cticos y sem´nticos de la interfaz. a a He 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 a lenguajes.
  • 223. 18.4 Uso de push y pop 197 class 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 ıa Para meter un elemento nuevo en la pila, push lo apila en elementos. Para quitar un elemento de la pila, pop utiliza el m´todo de lista hom´nimo2 para e o quitar 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 e m´todos existentes, se llama enchapado. En la vida real, un enchapado es una e capa fina de madera de alta calidad que se utiliza en la fabricaci´n de muebles o para ocultar madera de baja calidad. Los cient´ıficos inform´ticos utilizan esta a met´fora para describir una parte de un programa que esconde los detalles de a una implementaci´n y que provee una interfaz mas simple o mas est´ndar. o a 18.4. Uso de push y pop Una pila es una estructura gen´rica de datos, lo significa que se puede a˜adir e n cualquier tipo de elementos a ella. El ejemplo mete en la pila dos enteros y una cadena: >>> s = Stack() >>> s.push(54) >>> s.push(45) >>> s.push("+") Se pueden utilizar isEmpty y pop para quitar e imprimir todos los elementos en la pila: 2 del mismo nombre
  • 224. 198 Pilas while not s.isEmpty() : print s.pop(), La salida es + 45 54. En otras palabras, ¡se ha utilizado una pila para imprimir los elementos en orden inverso! Reconocemos que no es el formato est´ndar de a impresi´n de listas, pero fue muy f´cil usar una pila para lograrlo. o a Compare estas l´ ıneas con la implementaci´n de imprimeAlReves que vimos o en la Secci´n 17.4. Existe un paralelo natural entre la versi´n recurrente de o o imprimeAlReves y el algoritmo de pila que acabamos de ver. La diferencia es que imprimeAlReves utiliza la pila de tiempo de ejecuci´n para mantenerse al o tanto de los nodos mientras recorre la lista y luego los imprime cuando regresa de la recursi´n. El algoritmo de pila hace lo mismo, pero utiliza un objeto Pila o en vez de la pila de tiempo de ejecuci´n. o 18.5. Usar una pila para evaluar postfijo Las expresiones matem´ticas en la mayor´ de lenguajes de programaci´n se a ıa o escriben con el operador entre los dos operandos, de esta manera: 1+2. A este formato se le llama infijo. Algunas calculadoras utilizan un formato alternativo llamado 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 *. o Este ejemplo demuestra una de las ventajas de el formato postfijo—no hay necesidad de usar par´ntesis para controlar el orden de operaciones. Para obtener e el 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.
  • 225. 18.6 An´lisis sint´ctico a a 199 18.6. An´lisis sint´ctico a a Para implementar el algoritmo anterior, necesitamos ser capaces de recorrer una cadena y separarla en operandos y operadores. Este proceso es un ejemplo del an´lisis sint´ctico, y los resultados—la piezas individuales de la cadena—son a a tokens3 . 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 o y en el m´dulo re (expresiones regulares). La funci´n string.split parte una o o cadena y la convierte en una lista utilizando un s´lo car´cter como delimitador. o a Por 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 a espacio. La funci´n re.split tiene m´s potente, y nos permite utilizar una expresi´n o a o regular en vez de un delimitador. Una expresi´n regular es una manera de espe- o cificar un conjunto de cadenas. Por ejemplo, [A-z] es el conjunto de todas las letras 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 u lo 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; el delimitador 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 e 18.7. Evaluar un postfijo Para evaluar una expresi´n en formato postfijo usaremos el analizador sint´ctico o a de 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 o a˜adir´ confusi´n. n ıa o
  • 226. 200 Pilas las 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 lo dem´s es un operando. Por supuesto, ser´ mejor verificar si la entrada tiene a ıa errores y mostrar un mensaje con el error, pero eso se har´ despu´s. a e Comprobemos con una evaluaci´n de la forma postfijo (56+47)*2): o >>> print evalPostfijo ("56 47 + 2 *") 206 Esto es suficiente. 18.8. Clientes y proveedores Uno de los objetivos fundamentales de un TAD es el de separar los intereses del proveedor, quien escribe el c´digo de programa que implementa el TAD, o y los del cliente, quien utiliza el TAD. El proveedor s´lo se preocupa de la o implementaci´n y de si es correcta o no—de acuerdo a las especificaciones del o TAD—y no de c´mo se va a utilizar. o
  • 227. 18.9 Glosario 201 A la inversa, el cliente supone que la implementaci´n del TAD es correcta y o no se preocupa de los detalles. Cuando se usa uno de los tipos predefinidos de Python, 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 o go de cliente para probarlo. En ese case, uno toma ambos papeles, lo cual puede causar confusi´n. Tiene que fijarse bien en del papel que est´ tomando en todo o a momento. 18.9. Glosario tipo 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 a cliente: Un programa (o la persona que lo escribi´) que utiliza un TAD. o proveedor: 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. a estructura 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. e analizar sint´cticamente: Examinar una cadena de caracteres o tokens y a analizar su estructura gram´tical. a token: Un conjunto de caracteres que se tratan como una unidad y son anali- zados sint´cticamente, como las palabras de un lenguaje natural. a delimitador: Un car´cter utilizado para separar tokens, como la puntuaci´n a o en un lenguaje natural.
  • 229. Cap´ ıtulo 19 Colas Este 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 u mayor´ de los casos, el primer cliente de la fila es el primero al que se va a ıa servir. Sin embargo, hay excepciones. En los aeropuertos, a veces se saca de la cola a los clientes cuyos vuelos van a salir pronto. En los supermercados, un cliente 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 a La 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 a general es el encolamiento priorizado, en la que a cada cliente se le asigna una prioridad y el cliente con la prioridad m´s alta pasa primero, sin importar a el orden de llegada. Decimos que es la t´ctica m´s general porque la prioridad a a se puede basar en cualquier cosa: a qu´ hora sale el vuelo; cu´ntos productos e a lleva el cliente; cu´n importante es el cliente. Por supuesto, no todas las t´cticas a a de 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 a FIFO, y una cola priorizada (como su propio nombre indica) usa una t´ctica de a encolamiento priorizado. 19.1. El TAD Cola El TAD Cola se define a trav´s de las siguientes operaciones: e init : Inicializa una cola nueva vac´ ıa.
  • 230. 204 Colas inserta: A˜ade un elemento a la cola. n quita: Elimina y devuelve un elemento de la cola. El elemento devuelto es el primero que se a˜adi´. n o estaVacia: Comprueba si la cola est´ vac´ a ıa. 19.2. Cola Enlazada La primera implementaci´n del TAD Cola al que vamos a echar un vistazo se o llama cola enlazada porque est´ hecha de ojetos Nodo enlazados. He aqu´ la a ı definici´n de la clase: o class 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 carga Los m´todos estaVacia y quita son id´nticos a los m´todos estaVacia y e e e quitaPrimero de ListaEnlazada. El m´todo inserta es nuevo y un poco m´s e a
  • 231. 19.3 Rendimiento t´ ıpico 205 complicado. 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 de longitud deber´ ser el n´mero de nodos en la cola, y el ultimo nodo deber´ ıa u ´ ıa tener siguiente igual a None. Cr´ase que este m´todo cumple con ambas inva- e e riantes. 19.3. Rendimiento t´ ıpico Normalmente cuando invocamos un m´todo, no nos importan los detalles de su e implementaci´n. Pero hay un “detalle” que podr´ interesarnos: el rendimien- o ıa to t´ ıpico del m´todo. ¿Cu´nto tarda, y c´mo var´ el tiempo de ejecuci´n al e a o ıa o aumentar el n´mero de elementos de la colecci´n? u o Primero 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 e as´ se llama operaci´n de tiempo constante. En realidad, el m´todo podr´ ı o e ıa ser ligeramente m´s r´pido cuando la lista est´ vac´ porque se salta el cuerpo a a a ıa de la condici´n, pero esa diferencia no es significativa. o El rendimiento de inserta es muy diferente. En el caso general, tenemos que recorrer la lista para encontrar el ultimo elemento. ´ Este recorrido cuesta un tiempo proporcional a la longitud de la lista. Como el tiempo de ejecuci´n es funci´n lineal de la longitud, este m´todo se llama de o o e tiempo lineal. Comparado con el tiempo constante, es muy pobre. 19.4. Cola Enlazada Mejorada Nos gustar´ una implementaci´n del TAD Cola capaz de realizar todas las ıa o operaciones en tiempo constante. Una forma de hacerlo es modificar la clase Cola de modo que mantenga una referencia tanto al primero como al ultimo ´ nodo, como se muestra en la figura:
  • 232. 206 Colas head length 3 last cargo 1 cargo 2 cargo 3 next next next La 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 ´ e inserta 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 + 1 Como ultimo sigue el rastro del ultimo nodo, no necesitamos buscarlo. A causa ´ de esto, este m´todo funciona en tiempo constante. e Debemos pagar un precio por esa velocidad. Tenemos que a˜adir un caso especial n a quita para apuntar ultimo a None cuando quitamos el ultimo nodo: ´ class ColaMejorada:
  • 233. 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 carga Esta implementaci´n es m´s complicada que la de la Lista Enlazada, y es m´s o a a dif´ demostrar que es correcta. La ventaja es que hemos alcanzado la meta: ıcil tanto 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 priorizada El TAD Cola Priorizada tiene el mismo interfaz que el TAD Cola, pero diferente sem´ntica. De nuevo, el interfaz es: a init : Inicializa una cola vac´ nueva. ıa inserta: A˜ade un nuevo elemento a la cola. n quita: Elimina y devuelve un elemento de la cola. El elemento devuelto es el de prioridad m´s alta. a estaVacia: Comprueba si la cola est´ vac´ a ıa. La diferencia sem´ntica es que el elemento eliminado de la cola no es necesa- a riamente el primero que se a˜adi´. En su lugar, es el elemento con la prioridad n o m´s alta. Lo que son las prioridades y c´mo se comparan con las otras no se a o especifica en la implementaci´n de la Cola Priorizada. Depende de los elementos o de la cola. Por ejemplo, si los elementos de la cola tienen nombres, podemos elegirlos en orden alfab´tico. Si son puntuaciones de bolos, podemos ir de mayor a menor, e pero si son puntuaciones de golf, iremos de menor a mayor. Siempre que podamos comparar los elementos de la cola, podremos encontrar y quitar el elemento con mayor prioridad. Esta implementaci´n de Cola Priorizada tiene como atributo una lista de Python o que contiene los elementos de la cola.
  • 234. 208 Colas class 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 o operaciones sobre listas. El unico m´todo interesante es quita: ´ e class 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 elemento Al 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- a pleta el bucle, el programa compara el i´simo elemento con el campe´n. Si el e o nuevo 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 elemento mayor. 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 a 14 13 12 11
  • 235. 19.6 La clase Golfista 209 Si la cola contiene n´meros o cadenas simples, se eliminan en orden num´rico u e o alfab´tico, de mayor a menor. Python puede encontrar el entero o la cadena e mayor porque puede compararlos usando los operadores de comparaci´n inter- o nos. Si la cola contiene un tipo de objeto, debe proporcionar un m´todo cmp . e Cuando quita usa el operador > para comparar elementos, invoca al cmp de uno 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´. a 19.6. La clase Golfista Como ejemplo de un objeto con una definici´n inusual de prioridad, vamos a o implementar 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 puntuaciones en bonitas columnas. A continuaci´n definimos una versi´n de cmp en la que la puntuaci´n m´s o o o a baja 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 0 Ya 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) >>>
  • 236. 210 Colas >>> cp = ColaPriorizada() >>> cp.inserta(tiger) >>> cp.inserta(cabr) >>> cp.inserta(ola) >>> while not cp.estaVacia(): print cp.quita() Tiger Woods : 61 J.M. Olaz´bal : 69 a ´ngel Cabrera : 72 A 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. o 19.7. Glosario cola: Un conjunto ordenado de objetos esperando un servicio de alg´n tipo. u Cola: 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 o FIFO: “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. n tiempo 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
  • 237. Cap´ ıtulo 20 ´ Arboles Al igual que las listas enlazadas, los ´rboles est´n hechos de nodos. Un tipo a a com´n de ´rbol es un ´rbol binario, en el que cada nodo contiene una referencia u a a a otros dos nodos (posiblemente nula). Nombramos a estas referencias como sub´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 None Para 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 a extremos con referencias nulas se llaman hojas. Puede parecer extra˜o quen dibujemos la figura con la ra´ arriba y las hojas abajo, pero eso no es lo m´s ız a raro. Para empeorar las cosas, los cient´ıficos inform´ticos a˜aden a la mezcla otra a n met´fofa: el ´rbol geneal´gico. El nodo superior se llama a veces padre y los a a o
  • 238. 212 ´ Arboles nodos a los que se refiere son sus hijos. Los nodos con el mismo padre se llaman hermanos. Para terminar, tenemos un vocabulario geom´trico para hablar sobre los ´rboles. e a Ya hemos mencionado izquierda y derecha, pero tambi´n est´n “arriba” (hacia e a el padre/ra´ y “abajo” (hacia los hijos/hojas). Tambi´n, todos los nodos que ız) e est´n a la misma distancia de la ra´ forman un nivel del ´rbol. a ız a Probablemente no sean necesarias las met´foras arb´reas para hablar de ´rboles, a o a pero ah´ est´n. ı a Igual que las listas enlazadas, los ´rboles son estructuras de datos recursivas a porque 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. a 20.1. Crear ´rboles a El proceso de montar un ´rbol es similar al proceso de montar una lista enlazada. a Cada invocaci´n del constructor crea un solo nodo. o class 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 a deben ser ´rboles. Tanto izquierda como derecha son opcionales; el valor por a omisi´n es None. o Para imprimir un nodo, simplemente imprimimos la carga. Una forma de construir un ´rbol es del fondo hacia arriba. Asigne primero los a nodos hijos: izquierda = Arbol(2) derecha = Arbol(3)
  • 239. 20.2 Recorrer ´rboles a 213 Luego cree el nodo padre y vinc´lelo a los hijos: u arbol = Arbol(1, izquierda, derecha); Podemos escribir este c´digo m´s concisamente anidando las invocaciones al o a constructor: >>> 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 a Siempre que usted vea una nueva estructura de datos, su primera pregunta deber´ ser: “¿C´mo la recorro?” La forma m´s natural de recorrer un ´rbol ıa o a a es recursivamente. Por ejemplo, si el ´rbol contiene enteros como carga, esta a funci´n nos devuelve su suma: o def total(arbol): if arbol == None: return 0 return total(arbol.izquierda) + total(arbol.derecha) + arbol.carga El 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 a hijos. Cuando terminan las llamadas recursivas, sumamos la carga del padre y devolvemos el total. 20.3. ´ Arboles de expresi´n o Un ´rbol es un forma natural de representar la estructura de una expresi´n. a o Al contrario que otras notaciones, puede representar el c´lculo de forma no a ambigua. Por ejemplo, la expresi´n infija 1 + 2 * 3 es ambigua a no ser que o sepamos que la multiplicaci´n se realiza antes que la suma. o ´ Este ´rbol de expresi´n representa el mismo c´lculo: a o a
  • 240. 214 ´ Arboles tree cargo + left right cargo 1 cargo left right left * right cargo 2 cargo 3 left right left right Los nodos de un ´rbol de expresi´n pueden ser operandos como 1 y 2 u operado- a o res como + y *. Los operandos son nodos hojas; los nodos operadores contienen referencias 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 o se 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 se a usan dentro de los compiladores para analizar, optimizar y traducir programas. 20.4. Recorrido de un ´rbol a Podemos 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 a ra´ luego el sub´rbol izquierdo entero y despu´s el sub´rbol derecho entero. ız, a e a Esta forma de recorrer un ´rbol se llama orden prefijo, porque el contenido de a la ra´ aparece antes del contenido de los hijos. La salida del ejemplo anterior ız es:
  • 241. 20.4 Recorrido de un ´rbol a 215 >>> arbol = Arbol(’+’, Arbol(1), Arbol(’*’, Arbol(2), Arbol(3))) >>> imprimeArbol(arbol) + 1 * 2 3 Este formato es diferente tanto del postfijo como del infijo; es otra notaci´n o llamada 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 a si´n en una notaci´n diferente. Por ejemplo, si imprime primero los sub´rboles o o a y luego la ra´ tendr´: ız, a def 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 o llama orden postfijo. Finalmente, para recorrer un ´rbol en orden infijo, usted imprime el ´rbol a a izquierdo, luego la ra´ y despu´s el ´rbol derecho: ız e a def 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 o Para ser justos debemos se˜alar que hemos omitido una importante compli- n caci´n. A veces, al escribir una expresi´n infija, debemos usar par´ntesis para o o e preservar el orden de las operacioens. De modo que una exploraci´n de orden o infijo no es suficiente para generar una expresi´n infija. o No obstante, con unas peque˜as mejoras, el ´rbol de expresi´n y los tres re- n a o corridos nos dan una forma general de traducr expresiones de un formato a otro. 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 e Si 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
  • 242. 216 ´ Arboles def 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 o incialmente es 0. Cada vez que hacemos una llamada recursiva, pasamos nivel+1 porque el nivel del hijo es siempre uno mayor que el del padre. Sangramos cada elemento con dos espacios por nivel. El resultado del ´rbol de ejemplo es: a >>> imprimeArbolSangrado(arbol) 3 * 2 + 1 Si mira la salida de lado, ver´ una versi´n simplificada de la figura original. a o 20.5. Construir un ´rbol de expresi´n a o En 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 siguiente a o o ´rbol: a * + 9 3 7 F´ ıjese en que hemos simplificando el diagrama omitiendo los nombres de los atributos. El analizador que vamos a escribir maneja expresiones que incluyen n´meros, u par´ntesis y los operadores + y *. Suponemos que la cadena de entrada ya ha e sido tokenizada y almacenada en una lista de Python. La lista de tokens de (3+7)*9 es:
  • 243. 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 a lista. A modo de ejercicio, escriba una funci´n que tome una cadena con- o teniendo una expresi´n y devuelva una lista de tokens. o La primera funci´n que vamos a escribir es tomaToken, que toma como par´me- o a tros una lista de tokens y el token que esperamos. Compara el token esperado con el primer token de la lista: si coinciden, elimina el token de la lista y devuelve verdadero, en caso contrario, devuelve falso: def tomaToken(listaToken, esperado): if listaToken[0] == esperado: del listaToken[0] return 1 else: return 0 Como 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 o de listaToken es un n´mero, obtieneNumero lo elimina y devuelve un nodo u hoja que contiene el n´mero; en caso contrario, devuelve None. u def 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 una lista de n´meros a listaToken, extraemos el primero, imprimimos el resultado u e 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 como a o u operandos, como 3 * 7.
  • 244. 218 ´ Arboles Aqu´ tenemos una versi´n de obtieneProducto que maneja productos simples. ı o def obtieneProducto(listaToken): a = obtieneNumero(listaToken) if tomaToken(listaToken, ’*’): b = obtieneNumero(listaToken) return Arbol (’*’, a, b) else: return a Suponiendo que obtieneNumero se ejecuta con ´xito y devuelve un ´rbol simple, e a asignamos el primer operando a a. Si el siguiente car´cter es *, obtenemos el a segundo n´mero y construimos un ´rbol de expresi´n con a, b, y el operador. u a o Si el siguiente car´cter es cualquier otra cosa, simplemente devolvemos el nodo a hoja 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) 9 El segundo ejemplo implica que entendemos un operando suelto como un tipo de producto. Esta definici´n de “producto” es contraria a la intuici´n, pero resulta o o ser util. ´ Ahora tenemos que v´rnoslas con productos compuestos, como 3 * 5 * 13. e Tratamos esta expresi´n como un producto de productos, es decir, 3 * (5 * o 13). El ´rbol resultante es: a * 3 * 5 13 Con un peque˜o cambio en obtieneProducto podemos manejar productos ar- n bitrariamente largos:
  • 245. 20.5 Construir un ´rbol de expresi´n a o 219 def obtieneProducto(listaToken): a = obtieneNumero(listaToken) if tomaToken(listaToken, ’*’): b = obtieneProducto(listaToken) # cambiamos esta l´nea ı return Arbol (’*’, a, b) else: return a En otras palabras, un producto puede ser bien un operando aislado o un ´rbol a con * en su ra´ un n´mero en la derecha y un producto en la izquierda. Este ız, u tipo de definici´n recursiva deber´ empezar a resultar familiar. o ıa Comprobemos 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- n ci´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, e una suma puede ser simplemente un producto. Si quiere seguir adelante con esta definici´n, tiene una bonita propiedad: pode- o mos representar cualquier expresi´n (sin par´ntesis) como una suma de produc- o e tos. Esta propiedad es la base de nuestro algoritmo de an´lisis. a obtieneSuma intenta construir un ´rbol con un producto en la izquierda y una a suma 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 a Vamos a probarla con 9 * 11 + 5 * 7: >>> listaToken = [9, ’*’, 11, ’+’, 5, ’*’, 7, ’fin’] >>> arbol = obtieneSuma(listaToken) >>> imprimeArbolPostfijo(arbol) 9 11 * 5 7 * +
  • 246. 220 ´ Arboles Ya casi hemos acabado, pero todav´ tenemos que manejar los par´ntesis. ıa e En culaquier lugar de una expresi´n donde puede haber un n´mero, puede o u haber tambi´n una suma entera entre par´ntesis. S´lo necesitamos modificar e e o obtieneNumero 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 e multiplicaci´n. o En la versi´n final del programa, ser´ bueno dar a obtieneNumero un nombre o ıa m´s descriptivo de su nueva funci´n. a o 20.6. Manejar errores En todo momento hemos supuesto que las expresiones estaban bien formadas. Por ejemplo, cuando alcanzamos el final de una subexpresi´n, suponemos que o el car´cter siguiente es un par´ntesis cerrado. Si hay un error y el siguiente a e car´cter es cualquier otra cosa, deber´ a ıamos ocuparnos de ´l. e def obtieneNumero(listaToken): if tomaToken(listaToken, ’(’): x = obtieneSuma(listaToken) if not tomaToken(listaToken, ’)’): raise ’ExpresionErronea’, ’falta un par´ntesis’ e return x
  • 247. 20.7 El ´rbol de animales a 221 else: # omitido el resto de la funci´n o La sentencia raise crea una excepci´n; en este caso creamos un nuevo tipo de ex- o cepci´n, llamado ExpresionErronea. Si la funci´n que llam´ a obtieneNumero, o o o o alguna de las otras funciones de la pila de llamadas, maneja la expresi´n, el o programa podr´ continuar. En caso contrario, Python imprimir´ un mensaje de a a error 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. o 20.7. El ´rbol de animales a En esta secci´n desarrollaremos un peque˜o programa que usa un ´rbol para o n a representar una base de conocimientos. El programa interact´a con el usuario para crear un ´rbol de preguntas y nom- u a bres de animales. Aqu´ tenemos un ejemplo de ejecuci´n: ı o Est´s pensando en un animal? s a Es un p´jaro? n a C´mo se llama el animal? perro o Qu´ pregunta distinguir´a a un perro de un p´jaro? Puede volar e ı a Si el animal fuera un perro, cu´l ser´a la respuesta? n a ı Est´s pensando en un animal? s a Puede volar? n Es un perro? n C´mo se llama el animal? gato o Qu´ 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 a Puede volar? n Ladra? s Es un perro? s Soy el m´s grande! a
  • 248. 222 ´ Arboles Est´s pensando en un animal? n a Este es el ´rbol que construye este di´logo: a a Can it fly? n y Does it bark? bird n y cat dog Al principio de cada ronda, el programa empieza en lo alto del ´rbol y hace la a primera pregunta. Dependiendo de la respuesta, se mueve al hijo de la izquierda o de la derecha y sigue hasta que llega a un nodo hoja. En ese momento, intenta adivinar. Si falla, pide al usuario el nombre del nuevo animal y una pregunta que distinga al intento fallido del nuevo animal. Entonces a˜ade un nodo al ´rbol n a con la nueva pregunta y el nuevo animal. ´ Este es el c´digo: o def 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
  • 249. 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 o usuario. Si la respuesta comienza con s o S, la funci´n devuelve verdadero: o def 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 a ejecute la sentencia break cuando el usuario ya no piense en ning´n animal. u El bucle while interno recorre el ´rbol de arriba a abajo, guiado por las res- a puestas del usuario. Cuando se a˜ade un nuevo nodo al ´rbol, la pregunta sustituye a la carga y los n a dos hijos son el animal nuevo y la carga original. Una carencia del programa es que, al salir, ¡olvida todo lo que usted le hab´ ıa ense˜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
  • 250. 224 ´ Arboles 20.8. Glosario ´rbol binario: Un ´rbol en el que cada nodo apunta a cero, uno, o dos nodos a a dependientes. ra´ El nodo superior de un ´rbol, sin padre. ız: a hoja: Un nodo del extremo inferior de un ´rbol, sin hijos. a padre: 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. u nivel: 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. o orden 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
  • 251. Ap´ndice A e Depuraci´n o En 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 e Aunque las secciones que siguen est´n organizadas por tipos de error, algunas a t´cnicas son aplicables en m´s de una situaci´n. e a o A.1. Errores de sintaxis Los errores de sintaxis suelen ser f´ciles de arreglar una vez que averigua a lo que son. Desgraciadamente, muchas veces los mensajes de error no son
  • 252. 226 Depuraci´n o muy utiles. Los mensajes m´s comunes son SyntaxError: invalid syntax y ´ a SyntaxError: 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 o En realidad, le dice d´nde not´ el problema Python, que no es necesariamente o o donde est´ el error. A veces el error est´ antes de la localizaci´n del mensaje de a a o error, muchas veces en la l´ ınea anterior. Si est´ haciendo el programa incrementalmente, deber´ tener casi localizado el a ıa error. Estar´ en la ultima l´ a ´ ınea que a˜adi´. n o Si est´ usted copiando c´digo de un libro, comience comparando con atenci´n a o o su c´digo con el del libro. Compruebe cada car´cter. Al mismo tiempo, recuerde o a que el libro podr´ estar equivocado, as´ que si ve algo que parezca un error de ıa ı sintaxis, podr´ serlo. ıa He 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 ıa Si nada funciona, siga con la secci´n que sigue... o
  • 253. A.2 Errores en tiempo de ejecuci´n o 227 A.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 ıa usted y el compilador no miran el mismo c´digo. Compruebe su entorno de o programaci´n para asegurarse de que el programa que est´ editando es el que o a est´ intentando ejecutar Python. Si no est´ seguro, pruebe a poner un error de a a sintaxis obvio y deliberado al principio del programa. Ahora ejecute (o importe) de nuevo. Si el compilador no encuentra el nuevo error probalemente hay algo equivocado en el modo en que est´ configurado su entorno. a Si esto ocurre, puede enfrentarse a ello empezando de nuevo con un programa nuevo como “Hola, mundo”, y asegurarse de que puede hacer que funcione un programa conocido. Luego a˜ada gradualmente los trozos del programa nuevo n al que funciona. A.2. Errores en tiempo de ejecuci´n o Una vez que su programa es sint´cticamente correcto, Python pude importarlo a y al menos comenzar a ejecutarlo. ¿Qu´ podr´ ir mal? e ıa A.2.1. Mi programa no hace nada de nada. Este problema es muy com´n cuando su archivo consta de funciones y clases u pero en realidad no invoca nada para que empiece la ejecuci´n. Esto puede ser o intencionado cuando s´lo planea importar el m´dulo para suministrar clases y o o funciones. Sin no es intencionado, aseg´rese de que est´ llamando a una funci´n que inicie u a o la 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 a A.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 una recursi´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”.
  • 254. 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 a Bucle Infinito Si cree que tiene un bucle infinito y piensa que sabe qu´ bucle provoca el pro- e blema, a˜ada una sentencia print que imprima los valores de las variables de n la condici´n al final del bucle junto con el valor de la condici´n. o o Por 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) o Ahora, cuando ejecute el programa, ver´ tres l´ a ıneas de salida en cada vuelta del bucle. En la ultima vuelta el valor de la condici´n deber´ ser false. Si el bucle ´ o ıa sigue ejecut´ndose, podr´ ver los valores de x e y, y podr´ averiguar por qu´ no a a a e se actualizan correctamente. Recursi´n Infinita o Una recursi´n infinita casi siempre har´ que el programa se ejecute un rato y o a luego 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 o comience por asegurarse de que hay un caso b´sico. En otras palabras, deber´ a ıa
  • 255. A.2 Errores en tiempo de ejecuci´n o 229 haber una condici´n que haga que la funci´n devuelva un valor sin hacer otra o o llamada recursiva. Si no, necesita revisar el algoritmo y encontrar ese caso b´sico. a Si hay un caso b´sico pero el programa no parece llegar hasta ´l, a˜ada una a e n sentencia print que imprima los par´metros al principio de la funci´n o m´todo. a o e Cuando ahora ejecute el programa, ver´ unas pocas l´ a ıneas cada vez que se invoque la funci´n o m´todo y all´ ver´ los par´metros. Si los par´metros no se o e ı a a a acercan al caso b´sico, eso le dar´ alguna idea de por qu´ no lo hace. a a e Flujo de Ejecuci´n o Si no est´ seguro de qu´ curso sigue el flujo de ejecuci´n en su programa, a˜ada a e o n sentencias print al principio de cada funci´n con un mensaje como “entrando o en la funci´n turur´”, donde turur´ es el nombre de la funci´n. o u u o Cuando ahora ejecute el programa, imprimir´ una traza de cada funci´n a me- a o dida que las vaya invocando. A.2.3. Cuando ejecuto el programa recibo una excepci´n. o Si algo va mal durante la ejecuci´n, Python imprime un mensaje que incluye el o nombre de la excepci´n, la l´ o ınea del programa donde sucedi´ el problema y una o traza inversa. La traza inversa identifica la funci´n que se est´ ejecutando ahora y la funci´n o a o que 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 a donde se encuentra. Tambi´n incluye los n´meros de las l´ e u ıneas de sus archivos donde suceden todas esas llamadas. El primer paso es examinar el lugar del programa donde sucede el error y ver si puede adivinar lo que sucedi´. Estos son algunos de los errores en tiempo de o ejecuci´n m´s comunes: o a NameError: 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. o TypeError: 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.
  • 256. 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. a AttributeError: 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- o de terminar enterrado en informaci´n. Hay dos formas de atajar el problema: o simplificar 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 un formato que la haga m´s comprensible. a Para simplificar el programa puede hacer varias cosas. Primero, reducir la escala del problema en el que est´ trabajando el programa. Por ejemplo, si est´ orde- a a nando un vector, ordene un vector peque˜o. Si el programa acepta entradas del n usuario, dele la entrada m´s simple que provoque el problema. a Segundo, limpie el programa. Elimine el c´digo muerto y reorganice el programa o para 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 a a reescribir esa parte con una estructura m´s simple. Si sospecha de una funci´n a o grande, trate de trocearla en funciones menores y pru´belas separadamente. e
  • 257. A.3 Errores sem´nticos a 231 El proceso de encontrar el caso m´ınimo de prueba le llevar´ a menudo al error. a Si se encuentra con que un programa funciona en una situaci´n pero no en otra, o eso le dar´ una pista sobre lo que ocurre. a De forma parecida, la reescritura de una porci´n de c´digo puede ayudarle a o o encontrar errores sutiles. Si hace un cambio que a usted le parece que no afecta al programa, pero s´ lo hace, le dar´ una pista. ı a A.3. Errores sem´nticos a En cierto modo, los errores sem´nticos son los m´s dif´ a a ıciles de corregir, porque el compilador y el sistema de ejecuci´n no proporcionan informaci´n sobre lo o o que va mal. S´lo usted sabe lo que se supone que debe hacer el programa, y s´lo o o usted sabe que no lo est´ haciendo. a El primer paso es hacer una concexi´n entre el texto del programa y el compor- o tamiento que est´ usted viendo. Necesita una hip´tesis sobre lo que realmente a o est´ haciendo el programa. Una de las dificultades que nos encontramos para a ello es la alta velocidad de los computadores. A menudo desear´ ralentizar el progrma a una velocidad humana, y con al- ıa gunos programas depuradores podr´ hacerlo. Pero el tiempo que lleva colocar a unas sentencias print en los lugares adecuadoes suele ser menor que el que lleva configurar el depurador, poner y quitar puntos de interrupci´n y “hacer o caminar” 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.
  • 258. 232 Depuraci´n o Para programar necesitar´ tener un modelo mental de c´mo funcionan los pro- a o gramas. Si escribe un programa que no hace lo que espera de ´l, muchas veces e el problema no estar´ en el programa, sino en su modelo mental. a La mejor manera de corregir su modelo mental es dividiendo el programa en sus componentes (normalmente las funciones y m´todos) y probando cada compo- e nente de forma independiente. Una vez que encuentre la discrepancia entre su modelo y la realidad, podr´ solucionar el problema. a Por supuesto, deber´ ir haciendo y probando componentes tal como desarrolla ıa el programa. Si encuentra un problema, s´lo habr´ una peque˜a cantidad de o a n c´digo nuevo del que no sabe si est´ correcto. o a A.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 o dif´ ıciles de depurar. Suele ser una buena idea dividir una expesi´n compleja en o una 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 a tan documentaci´n adicional, y es m´s f´cil de depurar porque puede comprobar o a a los tipos de las variables intermedias y mostrar sus valores. Otro problema que puede suceder con las expresiones grandes es que el orden de evaluaci´n puede no ser el que usted esperaba. Por ejemplo, si est´ traduciendo o a la expresi´n 2π a Python, podr´ escribir: o x ıa y = x / 2 * math.pi; Eso no es correcto, porque la multiplicaci´n y la divisi´n tienen la misma prece- o o dencia y se eval´an de izquierd a derecha. As´ que esa expresi´n calcula xπ/2. u ı o Una buena forma de depurar expresiones es a˜adir par´ntesis para hacer expl´ n e ıci- to el orden de evaluaci´n: o
  • 259. 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 e grama 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 a las 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 a tunidad de imprimir el valor de retorno antes de volver. De nuevo, puede usar una variable temporal. Por ejemplo, en lugar de: return self.manos[i].eliminaCoincidencias() podr´ excribir: ıa cant = self.manos[i].eliminaCoincidencias() return cant Ahora 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 e Cuando est´ calmado, piense en el programa. ¿Qu´ es lo que hace? ¿Cu´les e e a pueden 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 e A veces lleva tiempo encontrar un error. Muchas veces encontramos errores cuando 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 de quedarse dormido.
  • 260. 234 Depuraci´n o A.3.5. No, de verdad necesito ayuda. Sucede. Incluso los mejores programadores se atascan de vez en cuando. A veces trabaja durante tanto tiempo en un programa que no puede ver el error. Lo que necesita es un par de ojos nuevos. Antes de llamar a andie, aseg´rese de que ha agotado las t´cnicas explicadas u e aqu´ Su programa deber´ ser tan simple como sea posible, y usted deber´ estar ı. ıa ıa trabajando con la entrada m´ ınima que provoca el error. Deber´ tener sentencias ıa print en los lugares adecuados (y lo que dicen deber´ ser comprensible). De- ıa ber´ entender el problema lo bastante bien como para describirlo sucintamente. ıa Cuando llame a alguien para que le ayude, aseg´rese de darles la informaci´n u o que 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 e Cuando encuentre el error, t´mese un momento para pensar acerca de lo que o podr´ haber hecho para encontrarlo m´s r´pido. La siguiente vez que vea algo ıa a a parecido, ser´ capaz de encontrar el error antes. a Recuerde, el objetivo no es s´lo hacer que el programa funciones. El objetivo es o aprender c´mo hacer funcionar al programa. o
  • 261. Ap´ndice B e Crear un nuevo tipo de datos Los lenguajes de programaci´n orientados a objetos permiten a los programado- o res crear nuevos tipos de datos que se comporten de manera muy parecida a los tipos de datos nativos. Exploraremos esta posibilidad construyendo una clase Fraccion que funcione de manera muy similar a los tipos num´ricos nativos, e enteros, enteros largos y flotantes. Las fracciones, tambi´n conocidas como n´meros racionales, son valores que e u pueden expresrse como la proporci´n entre dos n´meros enteros, tal como 5/6. o u Al n´mero superior se se le llama numerador y al inferior se se le llama deno- u minador. Comenzamos definiendo la clase Fraccion con un m´todo de inicializaci´n que e o nos surta de un numerador y un demonimador enteros: class Fraccion: def __init__(self, numerador, denominador=1): self.numerador = numerador self.denominador = denominador El denominador es opcional. Una Fraccion con un s´lo par´metro representa un o a n´mero entero. Si el numerador es n, construimos la fracci´n n/1. u o El siguente paso es escribir un m´todo str para que imprima las frac- e ciones de forma que tenga sentido. La forma natural de hacerlo es “numera- dor/denominador”:
  • 262. 236 Crear un nuevo tipo de datos class Fraccion: ... def __str__(self): return "%d/%d" % (self.numerador, self.denominador) Para probar lo que tenemos hasta ahora, lo ponemos en un fichero llamado Fraccion.py y lo importamos desde el int´rprete de Python. Entonces creamos e un objeto fracci´n y lo imprimimos. o >>> from Fraccion import fraccion >>> mortadela = Fraccion(5,6) >>> print "La fracci´n es", mortadela o La fracci´n es 5/6 o Como siempre, la funci´n print invoca impl´ o ıcitamente al m´todo e str . B.1. Multiplicaci´n de fracciones o Nos gustar´ poder aplicar las operaciones normales de suma, resta, multiplica- ıa ci´n y divisi´n a las fracciones. Para ello, podemos sobrecargar los operadores o o matem´ticos para los objetos de clase Fraccion. a Comenzaremos con la multiplicaci´n porque es la m´s f´cil de implementar. o a a Para multiplicar dos fraciones, creamos una nueva fracci´n cuyo numerador o es el producto de los numeradores de los operandos y cuyo denominador es el producto de los denominadores de los operandos. mul es el nombre que Python utiliza para el m´todo que sobrecarga al operador *: e class 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/24 Funciona, pero ¡podemos hacerlo mejor! Podemos ampliar el m´todo para ma- e nejar la multiplicaci´n por un entero. Usamos la funci´n type para ver si otro o o es un entero y convertirlo en una fracci´n en tal caso. o class Fraccion: ...
  • 263. 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 o ci´n es el operando de la izquierda. o >>> print Fraccion(5,6) * 4 20/6 >>> print 4 * Fraccion(5,6) TypeError: __mul__ nor __rmul__ defined for these operands Para evaluar un operador binario como la multiplicaci´n, Python comprueba o primero el operando de la izquierda para ver si proporciona un m´todo mul e que soporte el tipo del segundo operando. En este caso, el operador nativo de multiplicaci´n del entero no soporta fracciones. o Despu´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 provisto el m´todo rmul , por lo que falla. e Por 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 e evaluamos 4 * Fraccion(5,6), Python llamar´ al m´todo rmul del objeto a e Fraccion y le pasar´ 4 como par´metro: a a >>> print 4 * Fraccion(5,6) 20/6 Dado que rmul es lo mismo que mul , y mul puede manejar un par´me- a tro entero, ya est´ hecho. a B.2. Suma de fracciones La suma es m´s complicada que la multiplicaci´n, pero a´n es llevadera. La a o u suma de a/b y c/d es la fracci´n (a*d+c*b)/b*d. o Usando como modelo el c´digo de la multiplicaci´n, podemos escribir o o add y radd :
  • 264. 238 Crear un nuevo tipo de datos class Fraccion: ... def __add__(self, otro): if type(otro) == type(5): otro = Fraccion(otro) return Fraccion(self.numerador * otro.denominador + self.denominador * otro.numerador, self.denominador * otro.denominador) __radd__ = __add__ Podemos probar estos m´todos con Fracciones y enteros. e >>> print Fraccion(5,6) + Fraccion(5,6) 60/36 >>> print Fraccion(5,6) + 3 23/6 >>> print 2 + Fraccion(5,6) 17/6 Los dos primeros ejemplos llaman a add ; el ultimo llama a ´ radd . B.3. Algoritmo de Euclides En 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 a denominador por el m´ximo com´ n divisor (MCD) de ambos, que es 12. a u El resultado ser´ 5/3. ıa En general, siempre que creamos un nuevo objeto Fraccion, deber´ ıamos redu- cirlo dividiendo el numerador y el denominador por el MCD de ambos. Si la fracci´n ya est´ reducida, el MCD es 1. o a Euclides de Alejandr´ (aprox. 325–265 a. C.) prensent´ un algoritmo para en- ıa o contrar 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 o def mcd (m, n): if m % n == 0:
  • 265. 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 o divisibilidad. En la ultima l´ ´ ınea, lo usamos para calcular el resto de la divisi´n. o Dado que todas las operaciones que hemos escrito creaban un nuevo objeto Fraccion para devolver el resultado, podemos reducir todos los resultados mo- dificando el m´todo de inicializaci´n. e o class Fraccion: def __init__(self, numerador, denominador=1): m = mcd (numerador, denominador) self.numerador = numerador / m self.denominador = denominador / m Ahora siempre que creemos una Fraccion quedar´ reducida a su forma can´ni- a o ca: >>> Fraccion(100,-36) -25/9 Una caracter´ ıstica estupenda de mcd es que si la fracci´n es negativa, el signo o menos siempre se trasladar´ al numerador. a B.4. Comparar fracciones Supongamos que tenemos dos objetos Fraccion, a y b, y evaluamos a == b. La implemetaci´n por defecto de == comprueba la igualdad superficial, por lo que o s´lo devuelve true si a y b son el mismo objeto. o Queremos m´s bien devolver verdadero si a y b tienen el mismo valor —eso es, a igualdad 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 o vez proporcionando un m´todo cmp . e Por convenio, el m´todo cmp devuelve un n´mero negativo si self es menor e u que otro, zero si son lo mismo, y un n´mero positivo si self es mayor que otro. u La forma m´s simple de comparar dos fracciones es la multipicaci´n cruzada. a o Si a/b > c/d, entonces ad > bc. Con esto en mente, aqu´ est´ el c´digo para ı a o cmp :
  • 266. 240 Crear un nuevo tipo de datos class Fraccion: ... def __cmp__(self, otro): dif = (self.numerador * otro.denominador - otro.numerador * self.denominador) return dif Si self es mayor que otro, entonces dif ser´ positivo. Si otro is mayor, entonces a dif ser´ ngativo. Si son iguales, dif es cero. a B.5. Forzando la m´quina a Por supuesto, a´n no hemos terminado. Todav´ hemos de implementar la resta u ıa sobrecargando sub y la divisi´n sobrecargando div . o Una manera de manejar estas operaciones es implementar la negaci´n sobre- o cargando neg y la inversi´n sobrecargando invert . Entonces podemos o restar negando el segundo operando y sumando, y podemos dividir invirtiendo el segundo operando y multiplicando. Luego, hemos de suministrar los m´todos rsub y rdiv . Desgraciadamen- e te, no podemos usar el mismo truco que usamos para la suma y la multiplicaci´n, o porque la resta y la divisi´n no son conmutativas. No podemos igualar rsub o y rdiv a sub y div . En estas operaciones, el orden de los operandos tiene importancia. Para manejar la negaci´n unitaria, que es el uso del signo menos con un unico o ´ operando, sobrecargamos el m´todo neg . e Podemos computar potencias sobrecargando pow , pero la implementaci´n o tiene truco. Si el exponente no es un n´mero entero podr´ no ser posible u ıa representar 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 u puede representar como una fracci´n). Por lo tanto, no es f´cil escribir la ver- o a si´n m´s general de pow . o a Existe otra extensi´n a la clase Fraccion que cabr´ considerar. Hasta ahora, o ıa hemos asumido que el numerador y el denominador son enteros. Podr´ ıamos considerar 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.
  • 267. B.6 Glosario 241 B.6. Glosario m´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. o reducir: Cambiar la fracci´n a su forma equivalente con un MCD igual a 1. o negaci´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
  • 269. Ap´ndice C e Listados Completos de Python C.1. Clase Punto 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) + ’)’ 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):
  • 270. 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 Hora class 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
  • 271. C.3 Cartas, mazos y juegos 245 hora.segundos = segs return hora C.3. Cartas, mazos y juegos import random class 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 0 class 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
  • 272. 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 mano class Mano(Mazo): def __init__(self, nombre=""): self.cartas = [] self.nombre = nombre def agregaCarta(self,carta) : self.cartas.append(carta)
  • 273. 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 cant 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)) # repartimos los naipes self.mazo.repartir(self.manos) print "----- Se han repartido las cartas." self.muestraManos()
  • 274. 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
  • 275. C.4 Lists Enlazadas 249 C.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 segundo class Nodo: def __init__(self, carga=None, siguiente=None): self.carga = carga self.siguiente = siguiente def __str__(self): return str(self.carga)
  • 276. 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 + 1 C.5. Clase Pila class 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):
  • 277. 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 priorizadas class 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
  • 278. 252 Listados Completos de Python def quita(self) : carga = self.cabeza.carga self.cabeza = self.cabeza.next self.longitud = self.longitud - 1 return carga class 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 carga class ColaPriorizada : def __init__(self) : self.elementos = [] def vacia(self) :
  • 279. ´ 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 elemento class 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 0 C.7. ´ Arboles class 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
  • 280. 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 = derecha def total(arbol) : if arbol == None : return 0 return total(arbol.izquierda) + total(arbol.derecha) + arbol.carga def 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 o def tomaToken(listaToken, esperado): if listaToken[0] == esperado: listaToken[0:1] = [] # quita el token return 1 else:
  • 281. C.9 Adivina el animal 255 return 0 def obtieneProducto(listaToken) : a = obtieneNumero(listaToken) if tomaToken(listaToken, ’*’) : b = obtieneProducto(listaToken) return Arbol(’*’, a, b) else : return a def obtieneSuma(listaToken) : a = obtieneProducto(listaToken) if tomaToken(listaToken, ’+’) : b = obtieneSuma(listaToken) return Arbol(’+’, a, b) else : return a def 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 u C.9. Adivina el animal def 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
  • 282. 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 class class Fraccion: def __init__(self, numerador, denominador=1): m = mcd (numerador, denominador)
  • 283. 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)
  • 285. Ap´ndice D e Lecturas recomendadas Y 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 a general. Los ejemplos en este libro han sido deliberadamente simples, por lo que pueden no haber mostrado las capacidades m´s excitantes de Python. A continuaci´n a o exponemos una muestra de las extensiones de Python y sugerencias sobre sus usos. 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.
  • 286. 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. a D.1. Libros y sitios web sobre Python Aqu´ 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
  • 287. 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. a Y 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 general Las siguientes sugerencias sobre lecturas adicionales incluyen muchos de los libros favoritos de los autores. Estos tratan sobre buenas pr´cticas de progra- a maci´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.
  • 288. 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
  • 289. Ap´ndice E e GNU Free Documentation License Version 1.1, March 2000 Copyright c 2000 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The purpose of this License is to make a manual, textbook, or other written do- cument “free” in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and pu- blisher a way to get credit for their work, while not being considered responsible for modifications made by others. This License is a kind of “copyleft,” which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software. We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this
  • 290. 264 GNU Free Documentation License License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference. E.1. Applicability and Definitions This License applies to any manual or other work that contains a notice placed by 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 member of 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/or translated into another language. A “Secondary Section” is a named appendix or a front-matter section of the Document 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. (For example, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of 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 the Document 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 is released 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, whose contents can be viewed and edited directly and straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup has been designed to thwart or discourage subsequent modifica- tion by readers is not Transparent. A copy that is not “Transparent” is called “Opaque.”
  • 291. E.2 Verbatim Copying 265 Examples of suitable formats for Transparent copies include plain ASCII wit- hout markup, Texinfo input format, L TEX input format, SGML or XML using A a publicly available DTD, and standard-conforming simple HTML designed for human modification. Opaque formats include PostScript, PDF, proprietary for- mats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally availa- ble, and the machine-generated HTML produced by some word processors for output purposes only. The “Title Page” means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, “Title Page” means the text near the most prominent appearance of the work’s title, preceding the beginning of the body of the text. E.2. Verbatim Copying You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in Section 3. You may also lend copies, under the same conditions stated above, and you may publicly display copies. E.3. Copying in Quantity If you publish printed copies of the Document numbering more than 100, and the Document’s license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
  • 292. 266 GNU Free Documentation License If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages. If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a publicly acces- sible computer-network location containing a complete Transparent copy of the Document, free of added material, which the general network-using public has access to download anonymously at no charge using public-standard network protocols. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public. It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document. E.4. Modifications You may copy and distribute a Modified Version of the Document under the conditions of Sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in 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.
  • 293. 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 that qualify 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.
  • 294. 268 GNU Free Documentation License To 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 nothing but endorsements of your Modified Version by various parties—for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard. You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back- Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one. The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version. E.5. Combining Documents You may combine the Document with other documents released under this Li- cense, under the terms defined in Section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice. The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the 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 else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work. In the combination, you must combine any sections entitled “History” in the various 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.”
  • 295. E.6 Collections of Documents 269 E.6. Collections of Documents You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in 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 each of the documents in all other respects. You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document. E.7. Aggregation with Independent Works A 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 distribution medium, does not as a whole count as a Modified Version of the Document, provided no compilation copyright is claimed for the compilation. Such a com- pilation is called an “aggregate,” and this License does not apply to the other self-contained works thus compiled with the Document, on account of their being thus compiled, if they are not themselves derivative works of the Document. If the Cover Text requirement of Section 3 is applicable to these copies of the Document, 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 the Document within the aggregate. Otherwise they must appear on covers around the whole aggregate. E.8. Translation Translation is considered a kind of modification, so you may distribute transla- tions of the Document under the terms of Section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License provided that you also include the original English version of this License. In case of a disagreement between the translation and the original English version of this License, the original English version will prevail.
  • 296. 270 GNU Free Documentation License E.9. Termination You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense, or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. E.10. Future Revisions of This License The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or 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 later version.applies to it, you have the option of following the terms and conditions either 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 Documents To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the 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.”
  • 297. E.11 Addendum: How to Use This License for Your Documents 271 If you have no Invariant Sections, write “with no Invariant Sections” instead of 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 for Back-Cover Texts. If your document contains nontrivial examples of program code, we recommend releasing 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.
  • 299. ´ Indice alfab´tico e ´rbol, 211 a 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, 224 a asignaci´n de tuplas, 100, 107, 168 o ´rbol de expresi´n, 213, 216 a o asignaci´n m´ltiple, 61, 73 o u ´ ındice, 76, 84, 97, 109, 229 asignmaci´no negativo, 76 tupla, 107 Make Way for Ducklings, 77 atributo, 139 Referencia de la Biblioteca de Pyt- clase, 163, 170 hon, 83 atributo de clase, 163, 170 atributos, 132 , 33 AttributeError, 230 abeced´rico, 77 a acceso, 86 barajar, 167 acto de fe, 55, 186 base de conocimiento, 221 acumulador, 167, 170, 178 bifurcaci´n condicional, 37 o aleatorio, 167 bloque, 37, 45 algoritmo, 10, 146, 148 booleana alias, 92, 97 expresi´n, 36 o ambig¨edad, 7, 134 u booleanas teorema fundamental, 188 funciones, 52 an´lisis sint´ctico, 10 a a borrado analizar, 199, 216 lista, 91 analizar sint´cticamente, 7, 201 a borrado en una lista, 91 andamiaje, 48, 59 bucle, 63, 73 anidamiento, 45 anidado, 165 archivo, 128 condici´n, 228 o texto, 121 cuerpo, 63, 73 archivo de texto, 121, 128 en una lista, 187 archivos, 119 for, 76 argumento, 21, 33 infinito, 63, 73, 228 argumentos, 28 recorrido, 76
  • 300. 274 ´ Indice alfab´tico e variable de, 73 clonar, 97 while, 62 codificar, 162, 170 bucle for, 76, 88 coerci´n, 33 o bucle infinito, 227, 228 tipo, 115 buffer circular, 210 coerci´n de tipos, 22 o coercion, 23 c´digo de objeto, 10 o coincidencia de esquemas, 107 c´digo ejecutable, 10 o cola, 203, 210 c´digo fuente, 10 o implementaci´n con Lista, 203 o c´digo muerto, 48, 59 o implementaci´n enlazada, 204 o cadena, 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, 210 cadena de formato, 123, 128 TAD, 207 cadena inmutable, 79 colecci´n, 185, 196 o caja, 114 columna, 96 caja de funci´n, 114 o car´cter, 75 a coma flotante, 20, 131 carga, 183, 192, 211 comentario, 20 Carta, 161 comentarios, 19 caso base, 43, 45 comparable, 164 cifrar, 162 comparaci´n o clase, 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, 20 clase abstracta, 210 concatenation, 18 clase hija, 171, 182 condici´n, 63 o clase padre, 171, 172, 175, 182 condici´n, 45, 228 o clase Punto, 155 condici´n previa, 188, 192 o clasificaci´n o condiciones car´cter, 82 a encadenadas, 38 clasificaci´n de caracteres, 82 o condiciones encadenadas, 38 clave, 109, 117 constructor, 131, 139, 162 cliente, 196, 201 contador, 80, 84 clonado, 93, 112 conteo, 103
  • 301. ´ Indice alfab´tico e 275 conversi´n de tipo, 33 o m´todos, 111 e conversi´n de tipos, 22 o operaciones sobre, 110 copia profunda, 139 directorio, 125, 128 copia superficial, 139 dise˜o orientado a objetos, 171 n copiado, 112, 137 divisi´n de enteros, 16, 20, 22 o corresponder, 162 documentaci´n, 192 o cuelgue, 227 Doyle, Arthur Conan, 6 cuerpo, 37, 45 bucle, 63 ejecuci´n o cursor, 73 flujo, 229 ejecuci´n condicional, 37 o decrementar, 84 elemento, 85, 97 definici´n o eliminaci´n de naipes, 168 o circular, 53 encapsulaci´n, 136, 195, 200 o recursiva, 219 encapsulado, 67 definici´n circular, 53 o encapsular, 73 definici´n de funci´n, 25, 33 o o enchapado, 197 definici´n recursiva, 219 o encolamiento priorizado, 203 delimitador, 97, 125, 199, 201 encurtido, 125, 128 denominador, 235 enlace, 192 depuraci´n, 10, 225 o enteros depuraci´n (debugging), 4 o divisi´n, 22 o desarrollo largos, 115 incremental, 148 enteros largos, 115 planificado, 148 envoltorio, 192 desarrollo de progamas error encapsulado, 67 de sintaxis, 225 desarrollo de programas, 73 en tiempo de compilaci´n, 225 o generalizaci´n, 67 o en tiempo de ejecuci´n, 43, 225 o desarrollo de prototipos, 145 sem´ntico, 225, 231 a desarrollo incremental, 49, 59, 148 sintaxis, 5 desarrollo incremental de progra- tiempo de ejecuci´n, 5 o mas, 226 error (bug), 4 desarrollo planificado, 148 error de sintaxis, 225 desbordamiento, 115 error en tiempo de compilaci´n, 225 o determinista, 107 error en tiempo de ejecuci´n, 5, 10, o diagrama de estado, 13, 20 43, 76, 79, 87, 100, 111, diagrama de pila, 33 113, 115, 120, 124, 225, diagramas de pila, 30, 42 229 diccionario, 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), 10 diccionarios, 109 escalera de color, 174
  • 302. 276 ´ Indice alfab´tico e escape funci´n de Fibonacci, 113 o secuencia de, 73 funci´n factorial, 54, 57 o espacio en blanco, 84 funci´n gamma, 57 o estilo funcional de programaci´n, o funci´n pura, 142, 148 o 144, 148 funciones estructura anidada, 161 argumentos, 28 estructura de datos composici´n, 24, 51 o gen´rica, 196, 197 e llamadas, 21 recursiva, 183, 192, 212 par´metros, 28 a estructura de datos recursiva, 212 recursivas, 42 estructura gen´rica de datos, 196, e tuplas como valor de retorno, 197 101 estructura recursiva de datos, 183, funciones booleanas, 52 192 funciones matem´ticas, 23 a Euclides, 238 excepci´n, 5, 10, 126, 128, 225, 229 o generalizaci´n, 67 o expresi´n, 16, 20, 198 o generalizaci´n, 136, 146 o booleana, 45 generalizar, 73 grande y peliaguda, 232 Golfista, 209 expresi´n booleana, 36, 45 o gr´fico de llamadas, 114 a expresi´n regular, 199 o guardi´n, 59 a gui´n, 10 o Fibonacci function, 56 gui´n bajo, 14 o FIFO, 203, 210 fila, 96 herencia, 171, 182 float, 12 histograma, 106, 107, 116 flujo de ejecuci´n, 27, 33, 229 o Holmes, Sherlock, 6 formal lenguaje, 6 identidad, 134 forzado de tipo de datos, 115 igualdad, 134 fracci´n, 235 o igualdad profunda, 134, 139 comparaci´n, 239 o igualdad superficial, 134, 139 multiplicaci´n, 236 o implementaci´no suma, 237 Cola, 203 frangoso, 53 imponer, 160 funci´n, 71 o impresi´n o funci´n join, 96 o mano de cartas, 174 funci´n split, 96 o objeto mazo, 166 funci´n, 25, 33, 141, 150 o imprimir booleana, 169 objeto, 133, 150 definici´n, 25 o incrementar, 84 factorial, 54 IndexError, 230 funci´n booleana, 169 o indicador, 44, 45
  • 303. ´ Indice alfab´tico e 277 infijo, 198, 201 lenguaje formal, 6, 10 infinito lenguaje natural, 6, 10, 134 bucle, 63 lenguaje seguro, 5 infio, 213 Linux, 6 inmutable, 99 lista, 85, 97, 183 instancia, 133, 136, 139 anidada, 95, 112 objeto, 132, 150, 164 bien construida, 191 instancia de objeto, 164 bucle, 187 instancia de un objeto, 132, 150 bucle for, 88 instanciaci´n, 132 o como par´metro, 185 a instanciar, 139 de objetos, 165 instrucci´n, 4 o elemento, 86 int, 12 enlazada, 183, 192 Intel, 64 impresi´n, 185 o intercambio, 168 imprimir hacia atr´s, 186 a interfaz, 196, 210 infinita, 187 interpretar, 2, 10 longitud, 87 invariante, 191, 192 modificar, 189 invocar, 117 mutable, 90 invocar m´todos, 111 e pertenencia, 88 irracional, 240 porciones, 90 iteraci´n, 61, 62, 73 o recorrer recursivamente, 186 recorrido, 87, 185 juego lista anidada, 97, 112 animales, 221 lista enlazada, 183, 192 juego de los animales, 221 lista infinita, 187 KeyError, 230 ListaEnlazada, 190 listas l´gico o anidadas, 85, 95 operador, 36 clonado, 93 lanzar una excepci´n, 126, 128 o como par´metros, 94 a lenguaje, 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, 21 lenguaje completo, 53 local lenguaje de alto nivel, 2, 10 variable, 69 lenguaje de bajo nivel, 2, 10 locales lenguaje de programaci´n, 1 o variables, 29 lenguaje de programaci´n orientado o logaritmo, 64 a objetos, 149, 160 longitud, 87
  • 304. 278 ´ Indice alfab´tico e lowercase, 82 modifcador, 148 modificador, 143 m´ximo com´n divisor, 238, 241 a u modificar listas, 189 m´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, 166 m´todo append, 166 e n´mero u m´todo ayudante, 190, 192 e aleatorio, 101 m´todo de inicializaci´n, 154, 160, e o n´mero aleatorio, 101 u 165 NameError, 229 m´todo de lista, 116 e natural m´todo envoltorio, 190 e lenguaje, 6 m´todos de desarrollo e negaci´n, 240 o incremental, 49 negaci´n unitaria, 241 o m´todos de lista, 166 e nivel, 211, 224 m´todos sobre diccionarios, 111 e nodo, 183, 192, 211, 224 m´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, 224 m´dulo copy, 137 o nodo hoja, 211, 224 m´dulo string, 81, 83 o nodo padre, 211, 224 m´ltiple u nodo ra´ 211, 224 ız, asignaci´n, 73 o None, 48, 59 manejar errores, 220 notaci´n de punto, 111, 151, 154 o manejar una excepci´n, 126, 128 o nueva l´ınea, 73 manejo de errores, 220 numerador, 235 marco, 42 marco de funci´n, 42 o objeto, 91, 97, 131, 139 matem´ticas a mudable, 136 funciones, 23 objeto invariante, 191 matriz, 95 objeto mudable, 136 dispersa, 112 objetos mazo, 165 lista de, 165 McCloskey, Robert, 77 obst´culo al rendiminto, 210 a mensajes de error, 225 operaciones mismidad, 134 con listas, 89 modelo operaciones con listas, 89 mental, 232 operaciones sobre cadenas, 17 modelo mental, 232 operador, 16, 20
  • 305. ´ 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, 158 operador binario, 213, 224 pop, 197 operador condicional, 164 porci´n, 78, 84 o operador corchete, 75 porciones, 90 operador de formato, 123, 128, 209, portabilidad, 10 230 portable, 2 operador in, 88, 169 postfijo, 198, 201, 213 operador l´gico, 36 o precedencia, 20, 232 operador m´dulo, 35, 45, 173 o prefijo, 214, 224 operador matem´tico, 236 a print operador unitario, 240 sentencia, 9, 10 operadores prioridad, 209 para listas, 89 producto, 219 operando, 16, 20 producto interior, 156, 160 orden, 164 programa, 10 orden completo, 164 desarrollo de, 73 orden de evaluaci´n, 232 o programaci´n orientada a objetos, o orden de las operaciones, 17 149, 171 orden infijo, 214, 215, 224 prosa, 8 orden parcial, 164 proveedor, 196, 201 orden postfijo, 214, 215, 224 pseudoaleatorio, 107 orden prefijo, 214, 224 pseudoc´digo, 238 o push, 197 palabra reservada, 13, 20 palabras reservadas, 14 racional, 235 palo, 161 rama, 38, 45 papel random, 167 variable, 188 randrange, 167 par clave-valor, 109, 117 recorrer, 84, 185, 186, 213 par´metros, 28 a recorrido, 76, 80, 88, 176, 208, 214 par´metro, 33, 94, 133 a lista, 87 lista, 94 recorrido de lista, 97 pass recorrido eureka, 80 sentencia, 37 rect´ngulo, 135 a patr´n, 80 o recuento, 116 patr´n computacional, 80 o recursi´n, 213, 214 o Pentium, 64 infinita, 228
  • 306. 280 ´ Indice alfab´tico e recursi´n infinita, 227, 228 o sentencia except, 126, 128 recursividad, 40, 42, 45, 53, 55 sentencia pass, 37 caso base, 43 sentencia print, 9, 10, 230 infinita, 43, 57 sentencia return, 40, 233 recursividad infinita, 43, 45, 57 sentencia try, 126 redimiento, 205 sentencias reducir, 238, 241 bloque, 37 redundancia, 7 compuestas, 37 referencia, 183 sentencias compuestas, 37 incrustada, 183, 192 bloque de sentencias, 37 referencia empotrada, 211 cabecera, 37 referencia incrustada, 183, 192 cuerpo, 37 referencias sequencia, 85 alias, 92 singleton, 189, 190, 192 reglas de precedencia, 17, 20 sintaxis, 5, 10, 226 reparto de naipes, 173 sobrecarga, 160, 236 repetici´n o operador, 209 lista, 89 sobrecarga de operadores, 156, 160, runtime error, 76 164, 209 ruta, 125 soluci´n de problemas, 10 o subclase, 171, 175, 182 secuencia, 97 subexpresi´n, 220 o secuencia aritm´tica, 66 e suma, 219 secuencia de escape, 66, 73 fracci´n, 237 o secuencia geom´trica, 66 e sustituir, 164 seguro lenguaje, 5 t´ctica de encolamiento, 203, 210 a sem´ntica, 5, 10 a tablas, 64 error, 5 dos dimensiones, 66 sentencia, 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 temporales sentencia break, 121, 128 variables, 48 sentencia compuesta, 45 teorema sentencia condicional, 45 fundamental de la ambig¨edad, u sentencia continue, 122, 128 188
  • 307. ´ Indice alfab´tico e 281 teorema fundamental de la am- uppercase, 82 big¨edad, 192 u uso de alias, 137 tesis de Turing, 53 tiempo constante, 205, 210 valor, 20, 91, 161 tiempo lineal, 205, 210 valor de retorno, 21, 33, 47, 59, 136 tipo, 11, 12, 20 valores cadena, 12 tuplas, 101 float, 12 valores de retorno int, 12 tuplas, 101 tipo abstracto de datos |verTAD, value, 11 195 variable, 12, 20 tipo compuesto de datos, 84 bucle, 173 tipo 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, 33 tipo de datos compuesto, 75, 131 variable temporal, 232 tipo de datos definido por el usua- variables rio, 131 locales, 69 tipo de funci´n o variables locales, 29 modifcador, 143 variables temporales, 48 pura, 142 tipo inmutable, 107 while tipo mutable, 107 sentencia, 62 tipos whitespace, 82 coerci´n, 22 o comprobaci´n, 57 o conversi´n, 22 o tipos de datos enteros largos, 115 inmutables, 99 tuplas, 99 token, 199, 201, 216 traceback, 31 traza, 127 traza inversa, 43, 229 try, 128 tupla, 99, 107 tuplas, 101 Turing, Alan, 53 TypeError, 229 unidad, 10