0% encontró este documento útil (0 votos)
97 vistas584 páginas

Lenguajes Naturales vs. Lenguajes de Programación

Cargado por

ppbilly
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
97 vistas584 páginas

Lenguajes Naturales vs. Lenguajes de Programación

Cargado por

ppbilly
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 584

1.1.2.

2 Programación - Fundamentos Básicos

Lenguajes naturales vs. Lenguajes de programación


Un lenguaje es un medio (y una herramienta) para expresar y registrar pensamientos. Hay muchos
lenguajes a nuestro alrededor. Algunos de ellos no requieren hablar ni escribir, como el lenguaje
corporal. Es posible expresar tus sentimientos más profundos de manera muy precisa sin decir una
palabra.

Otro lenguaje que empleas cada día es tu lengua materna, que utilizas para manifestar tu voluntad y
para pensar en la realidad. Las computadoras también tienen su propio lenguaje, llamado
lenguaje máquina, el cual es muy rudimentario.

Una computadora, incluso la más técnicamente sofisticada, carece incluso de un rastro de


inteligencia. Se podría decir que es como un perro bien entrenado, responde solo a un conjunto
predeterminado de comandos conocidos.

Los comandos que reconoce son muy simples. Podemos imaginar que la computadora responde a
órdenes como "Toma ese número, divídelo por otro y guarda el resultado".

Un conjunto completo de comandos conocidos se llama lista de instrucciones, a veces


abreviada IL (por sus siglas en inglés de Instruction List). Los diferentes tipos de computadoras
pueden variar según el tamaño de sus IL y las instrucciones pueden ser completamente diferentes en
diferentes modelos.

Nota: los lenguajes máquina son desarrollados por humanos.

Ninguna computadora es actualmente capaz de crear un nuevo idioma. Sin embargo, eso puede
cambiar pronto. Por otro lado, las personas también usan varios idiomas muy diferentes, pero estos
idiomas se crearon ellos mismos. Además, todavía están evolucionando.

Cada día se crean nuevas palabras y desaparecen las viejas. Estos lenguajes se llaman lenguajes
naturales.

 1.1.2.3 Programación - Fundamentos Básicos

¿Qué hace a un lenguaje?


Podemos decir que cada idioma (máquina o natural, no importa) consta de los siguientes elementos:

ALFABETO

Un conjunto de símbolos utilizados para formar palabras de un determinado idioma (por ejemplo, el
alfabeto latino para el inglés, el alfabeto cirílico para el ruso, el kanji para el japonés, etc.).

LÉXICO
(También conocido como diccionario) un conjunto de palabras que el idioma ofrece a sus usuarios
(por ejemplo, la palabra "computadora" proviene del diccionario en inglés, mientras que "abcde" no;
la palabra "chat" está presente en los diccionarios de inglés y francés, pero sus significados son
diferentes.

SINTAXIS

Un conjunto de reglas (formales o informales, escritas o interpretadas intuitivamente) utilizadas para


precisar si una determinada cadena de palabras forma una oración válida (por ejemplo, "Soy una
serpiente" es una frase sintácticamente correcta, mientras que "Yo serpiente soy una" no lo es).

SEMÁNTICA

Un conjunto de reglas que determinan si una frase tiene sentido (por ejemplo, "Me comí una dona"
tiene sentido, pero "Una dona me comió" no lo tiene).

La IL es, de hecho, el alfabeto de un lenguaje máquina. Este es el conjunto de símbolos más


simple y principal que podemos usar para dar comandos a una computadora. Es la lengua materna de
la computadora.

Desafortunadamente, esta lengua está muy lejos de ser una lengua materna humana. Todos (tanto las
computadoras como los humanos) necesitamos algo más, un lenguaje común para las computadoras
y los seres humanos, o un puente entre los dos mundos diferentes.

Necesitamos un lenguaje en el que los humanos puedan escribir sus programas y un lenguaje que las
computadoras puedan usar para ejecutar los programas, que es mucho más complejo que el lenguaje
máquina y más sencillo que el lenguaje natural.

Tales lenguajes son a menudo llamados lenguajes de programación de alto nivel. Son algo similares
a los naturales en que usan símbolos, palabras y convenciones legibles para los humanos. Estos
lenguajes permiten a los humanos expresar comandos a computadoras que son mucho más
complejas que las ofrecidas por las IL.

Un programa escrito en un lenguaje de programación de alto nivel se llama código fuente (en


contraste con el código de máquina ejecutado por las computadoras). Del mismo modo, el archivo
que contiene el código fuente se llama archivo fuente.

 1.1.2.4 Programación - Fundamentos Básicos | Compilación vs.


Interpretación
 Compilación vs. Interpretación
La programación de computadora es el acto de establecer una secuencia de instrucciones con la cual
se causará el efecto deseado. El efecto podría ser diferente en cada caso específico: depende de la
imaginación, el conocimiento y la experiencia del programador.

Por supuesto, tal composición tiene que ser correcta en muchos sentidos, tales como:

 Alfabéticamente: Un programa debe escribirse en una secuencia de comandos reconocible,


por ejemplo, el Romano, Cirílico, etc.
 Léxicamente: Cada lenguaje de programación tiene su diccionario y necesitas dominarlo;
afortunadamente, es mucho más simple y más pequeño que el diccionario de cualquier
lenguaje natural.
 Sintácticamente: Cada idioma tiene sus reglas y deben ser obedecidas.
 Semánticamente: El programa tiene que tener sentido.

Desafortunadamente, un programador también puede cometer errores en cada uno de los cuatro
sentidos anteriores. Cada uno de ellos puede hacer que el programa se vuelva completamente inútil.

Supongamos que ha escrito correctamente un programa. ¿Cómo persuadimos a la computadora para


que la ejecute? Tienes que convertir tu programa en lenguaje máquina. Afortunadamente, la
traducción puede ser realizada por una computadora, haciendo que todo el proceso sea rápido y
eficiente.

Hay dos formas diferentes de transformar un programa de un lenguaje de programación de alto


nivel a un lenguaje de máquina:

COMPILACIÓN - El programa fuente se traduce una vez (sin embargo, esta ley debe repetirse cada
vez que se modifique el código fuente) obteniendo un archivo (por ejemplo, un archivo .exe si el
código está diseñado para ejecutarse en MS Windows) que contiene el código de la máquina; ahora
puedes distribuir el archivo en todo el mundo; el programa que realiza esta traducción se llama
compilador o traductor.

INTERPRETACIÓN - Tú (o cualquier usuario del código) puedes traducir el programa fuente cada
vez que se ejecute; el programa que realiza este tipo de transformación se denomina intérprete, ya
que interpreta el código cada vez que está destinado a ejecutarse; también significa que no puede
distribuir el código fuente tal como está, porque el usuario final también necesita que el intérprete lo
ejecute.

Debido a algunas razones muy fundamentales, un lenguaje de programación de alto nivel particular
está diseñado para caer en una de estas dos categorías.

Hay muy pocos idiomas que se pueden compilar e interpretar. Por lo general, un lenguaje de
programación se proyecta con este factor en la mente de sus constructores: ¿Se compilará o
interpretará?

 1.1.2.5 Programación - Fundamentos Básicos | Compilación vs.


Interpretación

¿Qué hace realmente el intérprete?


Supongamos una vez más que has escrito un programa. Ahora, existe como un archivo de
computadora: un programa de computadora es en realidad una pieza de texto, por lo que el código
fuente generalmente se coloca en archivos de texto. Nota: debe ser texto puro, sin ninguna
decoración, como diferentes fuentes, colores, imágenes incrustadas u otros medios. Ahora tienes que
invocar al intérprete y dejar que lea el archivo fuente.

El intérprete lee el código fuente de una manera que es común en la cultura occidental: de arriba
hacía abajo y de izquierda a derecha. Hay algunas excepciones: se cubrirán más adelante en el curso.
En primer lugar, el intérprete verifica si todas las líneas subsiguientes son correctas (utilizando los
cuatro aspectos tratados anteriormente).

Si el compilador encuentra un error, termina su trabajo inmediatamente. El único resultado en este


caso es un mensaje de error. El intérprete le informará dónde se encuentra el error y qué lo causó.
Sin embargo, estos mensajes pueden ser engañosos, ya que el intérprete no puede seguir tus
intenciones exactas y puede detectar errores a cierta distancia de tus causas reales.

Por ejemplo, si intentas usar una entidad de un nombre desconocido, causará un error, pero el error
se descubrirá en el lugar donde se intenta usar la entidad, no donde se introdujo el nombre de la
nueva entidad.

En otras palabras, la razón real generalmente se ubica un poco antes en el código, por ejemplo, en el
lugar donde se tuvo que informar al intérprete de que usaría la entidad del nombre.

Si la línea se ve bien, el intérprete intenta ejecutarla (nota: cada línea generalmente se ejecuta por
separado, por lo que el trío "Lectura - Verificación - Ejecución", pueden repetirse muchas veces, más
veces que el número real de líneas en el archivo fuente, como algunas partes del código pueden
ejecutarse más de una vez).

También es posible que una parte significativa del código se ejecute con éxito antes de que el
intérprete encuentre un error. Este es el comportamiento normal en este modelo de ejecución.

Puedes preguntar ahora: ¿Cuál es mejor? ¿El modelo de "compilación" o el modelo de


"interpretación"? No hay una respuesta obvia. Si hubiera habido, uno de estos modelos habría dejado
de existir hace mucho tiempo. Ambos tienen sus ventajas y sus desventajas.
 1.1.2.6 Programación - Fundamentos Básicos | Compilación vs.
Interpretación

Compilación vs. Interpretación - Ventajas y


Desventajas

COMPILACIÓN INTERPRETACIÓN
VENTAJAS  La ejecución del código  Puede ejecutar el código en cuanto
traducido suele ser más lo complete; no hay fases
rápida. adicionales de traducción.
 Solo el usuario debe tener el  El código se almacena utilizando el
compilador; el usuario final lenguaje de programación, no el de
puede usar el código sin él. la máquina; esto significa que puede
 El código traducido se ejecutarse en computadoras que
almacena en lenguaje utilizan diferentes lenguajes
máquina, ya que es muy máquina; no compila el código por
difícil de entender, es separado para cada arquitectura
probable que tus propios diferente.
inventos y trucos de
programación sigan siendo
COMPILACIÓN INTERPRETACIÓN
secreto.
 La compilación en sí misma
puede llevar mucho tiempo;  No esperes que la interpretación
es posible que no puedas incremente tu código a alta
ejecutar tu código velocidad: tu código compartirá la
inmediatamente después de potencia de la computadora con el
DESVENTAJA
cualquier modificación. intérprete, por lo que no puede ser
S
 Tienes que tener tantos realmente rápido.
compiladores como  Tanto tú como el usuario final
plataformas de hardware en deben tener el intérprete para
los que deseas que se ejecutar su código.
ejecute su código.

¿Qué significa todo esto para ti?

 Python es un lenguaje interpretado. Esto significa que hereda todas las ventajas y
desventajas descritas. Por supuesto, agrega algunas de sus características únicas a ambos
conjuntos.
 Si deseas programar en Python, necesitarás el intérprete de Python. No podrás ejecutar tu
código sin él. Afortunadamente, Python es gratis. Esta es una de sus ventajas más
importantes.

Debido a razones históricas, los lenguajes diseñados para ser utilizados en la manera de
interpretación a menudo se llaman lenguajes de programación, mientras que los programas fuente
codificados que los usan se llaman scripts.

 1.1.3.1 Python - una herramienta, no un reptil

¿Qué es Python?
Python es un lenguaje de programación de alto nivel, interpretado, orientado a objetos
y de uso generalizado con semántica dinámica, que se utiliza para la programación de
propósito general.

Y aunque puede que conozcas a la pitón como una gran serpiente, el nombre del
lenguaje de programación Python proviene de una vieja serie de comedia de la BBC
llamada Monty Python's Flying Circus.

En el apogeo de su éxito, el equipo de Monty Python estaba realizando sus escenas


para audiencias en vivo en todo el mundo, incluso en el Hollywood Bowl.

Dado que Monty Python es considerado uno de los dos nutrientes fundamentales
para un programador (el otro es la pizza), el creador de Python nombró el lenguaje en
honor del programa de televisión.
¿Quién creó Python?
Una de las características sorprendentes de Python es el hecho de que en realidad es
el trabajo de una persona. Por lo general, los grandes lenguajes de programación son
desarrollados y publicados por grandes compañías que emplean a muchos
profesionales, y debido a las normas de derechos de autor, es muy difícil nombrar a
cualquiera de las personas involucradas en el proyecto. Python es una excepción.

No hay muchos idiomas cuyos autores son conocidos por su nombre. Python fue
creado por Guido van Rossum, nacido en 1956 en Haarlem, Países Bajos. Por
supuesto, Guido van Rossum no desarrolló y evolucionó todos los componentes de
Python.

La velocidad con la que Python se ha extendido por todo el mundo es el resultado del
trabajo continuo de miles de (muy a menudo anónimos) programadores, evaluadores,
usuarios (muchos de ellos no son especialistas en TI) y entusiastas, pero hay que decir
que la primera idea (la semilla de la que brotó Python) llegó a una cabeza: la de Guido.

 1.1.3.2 Python - una herramienta, no un reptil

Un proyecto de programación por hobby


Las circunstancias en las que se creó Python son un poco desconcertantes. Según
Guido van Rossum:

En diciembre de 1989, estaba buscando un proyecto de programación de


"pasatiempo" que me mantendría ocupado durante la semana de Navidad. Mi oficina
(...) estaría cerrada, pero tenía una computadora en casa y no mucho más en mis
manos. Decidí escribir un intérprete para el nuevo lenguaje de scripting en el que
había estado pensando últimamente: un descendiente de ABC que atraería a los
hackers de Unix / C. Elegí Python como un título de trabajo para el proyecto, estando
en un estado de ánimo ligeramente irreverente (y un gran fanático de Monty Python's
Flying Circus).Guido van Rossum

Los objetivos de Python


En 1999, Guido van Rossum definió sus objetivos para Python:

 Un lenguaje fácil e intuitivo tan poderoso como los de los principales


competidores.
 De código abierto, para que cualquiera pueda contribuir a su desarrollo.
 El código que es tan comprensible como el inglés simple.
 Adecuado para tareas cotidianas, permitiendo tiempos de desarrollo cortos.
Unos 20 años después, está claro que todas estas intenciones se han cumplido.
Algunas fuentes dicen que Python es el lenguaje de programación más popular del
mundo, mientras que otros afirman que es el tercero o el quinto.

De cualquier manera, todavía ocupa un alto rango en el top ten de la PYPL Popularity
of Programming Language y la TIOBE Programming Community Index.

Python no es una lengua joven. Es maduro y digno de confianza. No es una maravilla


de un solo golpe. Es una estrella brillante en el firmamento de programación, y el
tiempo dedicado a aprender Python es una muy buena inversión.

 1.1.3.3 Python - una herramienta, no un reptil | ¿Por qué Python?

¿Qué hace especial a Python?


¿Por qué los programadores, jóvenes y viejos, experimentados y novatos, quieran
usarlo? ¿Cómo fue que las grandes empresas adoptaron Python e implementaron sus
productos estrella al usarlo?

Hay muchas razones. Ya hemos enumerado algunas de ellas, pero vamos a


enumerarlas de una manera más práctica:

 Es fácil de aprender - El tiempo necesario para aprender Python es más corto


que en muchos otros lenguajes; esto significa que es posible comenzar la
programación real más rápido.
 Es fácil de enseñar - La carga de trabajo de enseñanza es menor que la que
necesitan otros lenguajes; esto significa que el profesor puede poner más
énfasis en las técnicas de programación generales (independientes del
lenguaje), no gastando energía en trucos exóticos, extrañas excepciones y
reglas incomprensibles.
 Es fácil de utilizar - Para escribir software nuevo; a menudo es posible escribir
código más rápido cuando se usa Python.
 Es fácil de entender - A menudo, también es más fácil entender el código de
otra persona más rápido si está escrito en Python.
 Es fácil de obtener, instalar y desplegar - Python es gratuito, abierto y
multiplataforma; No todos los lenguajes pueden presumir de eso.

Por supuesto, Python también tiene sus inconvenientes:

 No es un demonio de la velocidad; Python no ofrece un rendimiento


excepcional.
 En algunos casos puede ser resistente a algunas técnicas de prueba más
simples, lo que puede significar que la depuración del código de Python puede
ser más difícil que con otros lenguajes. Afortunadamente, cometer errores
siempre es más difícil en Python.
También debe señalarse que Python no es la única solución de este tipo disponible en
el mercado de TI.

Tiene muchos seguidores, pero hay muchos que prefieren otros lenguajes y ni siquiera
consideran Python para sus proyectos.

 1.1.3.4 Python - una herramienta, no un reptil | ¿Por qué Python?

Rivales de Python
Python tiene dos competidores directos, con propiedades y predisposiciones
comparables. Estos son:

 Perl - un lenguaje de scripting originalmente escrito por Larry Wall.


 Ruby - un lenguaje de scripting originalmente escrito por Yukihiro Matsumoto.

El primero es más tradicional, más conservador que Python, y se parece a algunos de


los buenos lenguajes antiguos derivados del lenguaje de programación C clásico.

En contraste, este último es más innovador y está más lleno de ideas nuevas. Python
se encuentra en algún lugar entre estas dos creaciones.

Internet está lleno de foros con discusiones infinitas sobre la superioridad de uno de
estos tres sobre los otros, si deseas obtener más información sobre cada uno de ellos.

¿Dónde podemos ver a Python en acción?


Lo vemos todos los días y en casi todas partes. Se utiliza ampliamente para
implementar complejos servicios de Internet como motores de búsqueda,
almacenamiento en la nube y herramientas, redes sociales, etc. Cuando utilizas
cualquiera de estos servicios, en realidad estás muy cerca de Python.

Muchas herramientas de desarrollo se implementan en Python. Cada vez se


escriben mas aplicaciones de uso diario en Python. Muchos científicos han
abandonado las costosas herramientas patentadas y se han cambiado a Python.
Muchos evaluadores de proyectos de TI han comenzado a usar Python para llevar a
cabo procedimientos de prueba repetibles. La lista es larga.

¿Por qué no Python?


A pesar de la creciente popularidad de Python, todavía hay algunos nichos en los que
Python está ausente o rara vez se ve:

 Programación de bajo nivel (a veces llamada programación "cercana al


metal"): si deseas implementar un controlador o motor gráfico
extremadamente efectivo, no se usaría Python
 Aplicaciones para dispositivos móviles: este territorio aún está a la espera de
ser conquistado por Python, lo más probable es que suceda algún día.

 1.1.4.1 Python 2 vs. Python 3

Hay más de un Python


Hay dos tipos principales de Python, llamados Python 2 y Python 3.

Python 2 es una versión anterior del Python original. Su desarrollo se ha estancado


intencionalmente, aunque eso no significa que no haya actualizaciones. Por el
contrario, las actualizaciones se emiten de forma regular, pero no pretenden modificar
el idioma de manera significativa. Prefieren arreglar cualquier error recién descubierto
y agujeros de seguridad. La ruta de desarrollo de Python 2 ya ha llegado a un callejón
sin salida, pero Python 2 en sí todavía está muy vivo.

Python 3 es la versión más nueva (para ser precisos, la actual) del lenguaje. Está
atravesando su propio camino de evolución, creando sus propios estándares y
hábitos.

El primero es más tradicional, más conservador que Python, y se parece a algunos de


los buenos lenguajes antiguos derivados del lenguaje de programación C clásico.

Estas dos versiones de Python no son compatibles entre sí. Las secuencias de
comandos de Python 2 no se ejecutarán en un entorno de Python 3 y viceversa, por lo
que si deseas que un intérprete de Python 3 ejecute el código Python 2 anterior, la
única solución posible es volver a escribirlo, no desde cero, por supuesto. Como
grandes partes del código pueden permanecer intactas, pero tienes que revisar todo el
código para encontrar todas las incompatibilidades posibles. Desafortunadamente,
este proceso no puede ser completamente automatizado.

Es demasiado difícil, consume mucho tiempo, es demasiado caro y es demasiado


arriesgado migrar una aplicación Python 2 antigua a una nueva plataforma. Es posible
que reescribir el código le introduzca nuevos errores. Es más fácil y mas sensato dejar
estos sistemas solos y mejorar el intérprete existente, en lugar de intentar trabajar
dentro del código fuente que ya funciona.

Python 3 no es solo una versión mejorada de Python 2, es un lenguaje completamente


diferente, aunque es muy similar a su predecesor. Cuando se miran a distancia,
parecen ser los mismos, pero cuando se observan de cerca, se notan muchas
diferencias.

Si estás modificando una solución Python existente, entonces es muy probable que
esté codificada en Python 2. Esta es la razón por la que Python 2 todavía está en uso.
Hay demasiadas aplicaciones de Python 2 existentes para descartarlo por completo.

NOTA
Si se va a comenzar un nuevo proyecto de Python, deberías usar Python 3, esta es la
versión de Python que se usará durante este curso.

Es importante recordar que puede haber diferencias mayores o menores entre las
siguientes versiones de Python 3 (p. Ej., Python 3.6 introdujo claves de diccionario
ordenadas de forma predeterminada en la implementación de CPython). La buena
noticia es que todas las versiones más nuevas de Python 3 son compatibles con las
versiones anteriores de Python 3. Siempre que sea significativo e importante, siempre
intentaremos resaltar esas diferencias en el curso.

Todos los ejemplos de código que encontrarás durante el curso se han probado con
Python 3.4, Python 3.6 y Python 3.7.

 1.1.4.2 Hay más de un Python: CPython y Cython

Python alias CPython


Además de Python 2 y Python 3, hay más de una versión de cada uno.

En primer lugar, están los Pythons que mantienen las personas reunidas en torno a
PSF (Python Software Foundation), una comunidad que tiene como objetivo
desarrollar, mejorar, expandir y popularizar Python y su entorno. El presidente del PSF
es el propio Guido van Rossum, y por esta razón, estos Pythons se llaman canónicos.
También se consideran Pythons de referencia, ya que cualquier otra implementación
del lenguaje debe seguir todos los estándares establecidos por el PSF.

Guido van Rossum utilizó el lenguaje de programación "C" para implementar la


primera versión de su lenguaje y esta decisión aún está vigente. Todos los Pythons que
vienen del PSF están escritos en el lenguaje "C". Hay muchas razones para este
enfoque y tiene muchas consecuencias. Una de ellos (probablemente la más
importante) es que gracias a él, Python puede ser portado y migrado fácilmente a
todas las plataformas con la capacidad de compilar y ejecutar programas en lenguaje
"C" (virtualmente todas las plataformas tienen esta característica, lo que abre muchas
expansiones y oportunidades para Python).
Esta es la razón por la que la implementación de PSF a menudo se
denomina CPython. Este es el Python más influyente entre todos los Pythons del
mundo.

Cython
Otro miembro de la familia Python es Cython.

Cython es una de las posibles soluciones al rasgo de Python más doloroso: la falta de
eficiencia. Los cálculos matemáticos grandes y complejos pueden ser fácilmente
codificados en Python (mucho más fácil que en "C" o en cualquier otro lenguaje
tradicional), pero la ejecución del código resultante puede requerir mucho tiempo.

¿Cómo se reconcilian estas dos contradicciones? Una solución es escribir tus ideas
matemáticas usando Python, y cuando estés absolutamente seguro de que tu código
es correcto y produce resultados válidos, puedes traducirlo a "C". Ciertamente, "C" se
ejecutará mucho más rápido que Python puro.

Esto es lo que pretende hacer Cython: traducir automáticamente el código de Python


(limpio y claro, pero no demasiado rápido) al código "C" (complicado y hablador, pero
ágil).

 1.1.4.3 Hay más de un Python: Jython, PyPy, and RPython

Jython
Otra versión de Python se llama Jython.

"J" es para "Java". Imagina un Python escrito en Java en lugar de C. Esto es útil, por
ejemplo, si desarrollas sistemas grandes y complejos escritos completamente en Java y
deseas agregarles cierta flexibilidad de Python. El tradicional CPython puede ser difícil
de integrar en un entorno de este tipo, ya que C y Java viven en mundos
completamente diferentes y no comparten muchas ideas comunes.
Jython puede comunicarse con la infraestructura Java existente de manera más
efectiva. Es por esto que algunos proyectos lo encuentran útil y necesario.

Nota: la implementación actual de Jython sigue los estándares de Python 2. Hasta


ahora, no hay Jython conforme a Python 3.

PyPy y RPython
Echa un vistazo al logo de abajo. Es un rebus. ¿Puedes resolverlo?

Es un logotipo de PyPy - un Python dentro de un Python. En otras palabras, representa


un entorno de Python escrito en un lenguaje similar a Python
llamado RPython (Restricted Python). En realidad es un subconjunto de Python. El
código fuente de PyPy no se ejecuta de manera interpretativa, sino que se traduce al
lenguaje de programación C y luego se ejecuta por separado.

Esto es útil porque si deseas probar cualquier característica nueva que pueda ser o no
introducida en la implementación de Python, es más fácil verificarla con PyPy que con
CPython. Esta es la razón por la que PyPy es más una herramienta para las personas
que desarrollan Python que para el resto de los usuarios.

Esto no hace que PyPy sea menos importante o menos serio que CPython.

Además, PyPy es compatible con el lenguaje Python 3.

Hay muchos más Pythons diferentes en el mundo. Los encontrarás sí los buscas,
pero este curso se centrará en CPython

 1.2.1.1 Comienza tu viaje en Python

¿Cómo obtener Python y cómo usarlo?


Hay varias formas de obtener tu propia copia de Python 3, dependiendo del sistema
operativo que utilices.
Es probable que los usuarios de Linux tengan Python ya instalado - este es el
escenario más probable, ya que la infraestructura de Python se usa de forma intensiva
en muchos componentes del sistema operativo Linux.

Por ejemplo, algunas distribuciones pueden unir sus herramientas específicas con el
sistema y muchas de estas herramientas, como los administradores de paquetes, a
menudo están escritas en Python. Algunas partes de los entornos gráficos disponibles
en el mundo de Linux también pueden usar Python.

Si eres un usuario de Linux, abre la terminal/consola y escribe:

python3

En el indicador de shell, presiona Enter y espera.

Si ves algo como esto:

Python 3.4.5 (default, Jan 12 2017, 02:28:40)


[GCC 4.2.1 Compatible Clang 3.7.1 (tags/RELEASE_371/final)] on
linux
Type "help", "copyright", "credits" or "license" for more
information.
>>>

Entonces no tienes que hacer nada más.

Si Python 3 está ausente, consulta la documentación de Linux para saber cómo usar tu
administrador de paquetes para descargar e instalar un paquete nuevo: el que
necesitas se llama python3 o su nombre comienza con eso.

Todos los usuarios que no sean Linux pueden descargar una copia
en https://www.python.org/downloads/.

 1.2.1.2 Comienza tu viaje en Python | Descargando e instalando


Python

Descargando e instalando Python


Debido a que el navegador le dice al sitio web que se ingresó, el sistema operativo que
se utiliza, el único paso que se debe seguir es hacer clic en la versión de Python que se
desee.

En este caso, selecciona Python 3. El sitio siempre te ofrece la última versión.


Si eres un usuario de Windows , utiliza el archivo .exe descargado y sigue todos los
pasos.

Deja las configuraciones predeterminadas que el instalador sugiere por ahora, con
una excepción: mira la casilla de verificación denominada Agregar Python 3.x a
PATH y selecciónala.

Esto hará las cosas más fáciles.

Si eres un usuario de macOS , es posible que ya se haya preinstalado una versión de


Python 2 en tu computadora, pero como estaremos trabajando con Python 3, aún
deberás descargar e instalar el archivo .pkg correspondiente desde el sitio de Python.

 1.2.1.3 Comienza tu viaje en Python

Comenzando tu trabajo con Python


Ahora que tienes Python 3 instalado, es hora de verificar si funciona y de hacer el
primer uso.

Este será un procedimiento muy simple, pero debería ser suficiente para convencerte
de que el entorno de Python es completo y funcional.

Hay muchas formas de utilizar Python, especialmente si vas a ser un desarrollador de


Python.

Para comenzar tu trabajo, necesitas las siguientes herramientas:

 Un editor que te ayudará a escribir el código (debes tener algunas


características especiales, no disponibles en herramientas simples); este editor
dedicado te dará más que el equipo estándar del sistema operativo.
 Una consola en la que puedes iniciar tu código recién escrito y detenerlo por la
fuerza cuando se sale de control.
 Una herramienta llamada depurador, capaz de ejecutar tu código paso a paso
y te permite inspeccionarlo en cada momento de su ejecución.

Además de sus muchos componentes útiles, la instalación estándar de Python 3


contiene una aplicación muy simple pero extremadamente útil llamada IDLE.

IDLE es un acrónimo de: Integrated Development and Learning Environment


(Desarrollo Integrado y Entorno de Aprendizaje).

Navega por los menús de tu sistema operativo, encuentra IDLE en algún lugar debajo
de Python 3.x y ejecútalo

 1.2.1.4 Comienza tu viaje en Python


¿Cómo escribir y ejecutar tu primer programa?
Ahora es el momento de escribir y ejecutar tu primer programa en Python 3. Por
ahora, será muy simple.

El primer paso es crear un nuevo archivo fuente y llenarlo con el código. Haz clic
en File en el menú del IDLE y elige New File.

Como puedes ver, IDLE abre una nueva ventana para ti. Puedes usarla para escribir y
modificar tu código.

Esta es la ventana del editor. Su único propósito es ser un lugar de trabajo en el que
se trate tu código fuente. No confundas la ventana del editor con la ventana de shell.
Realizan diferentes funciones.

La ventana del editor actualmente no tiene título, pero es una buena práctica
comenzar a trabajar nombrando el archivo de origen.

Haz clic en File (en la nueva ventana), luego haz clic en Save as ... , selecciona una
carpeta para el nuevo archivo (el escritorio es un buen lugar para tus primeros
intentos de programación) y elige un nombre para el nuevo archivo.

Nota: no establezcas ninguna extensión para el nombre de archivo que vas a utilizar.
Python necesita que sus archivos tengan la extensión .py , por lo que debes confiar en
los valores predeterminados de la ventana de diálogo. El uso de la
extensión .py  estándar permite que el sistema operativo abra estos archivos
correctamente.

 1.2.1.5 Comienza tu viaje en Python

¿Cómo escribir y ejecutar tu primer programa?


Ahora pon solo una línea en tu ventana de editor recién abierta y con nombre.

La línea se ve así:

print("Hisssssss...")

Puedes utilizar el portapapeles para copiar el texto en el archivo.

No vamos a explicar el significado del programa en este momento. Encontrarás una


discusión detallada en el siguiente capítulo.
Echa un vistazo más de cerca a las comillas. Estas son la forma más simple de comillas
(neutral, recta, etc.) que se usan comúnmente en los archivos de origen. No intentes
utilizar citas tipográficas (curvadas, rizadas, etc.), utilizadas por los procesadores de
texto avanzados, ya que Python no las acepta.

Si todo va bien y no hay errores en el código, la ventana de la consola mostrará los


efectos causados por la ejecución del programa.

En este caso, el programa se ejecutara de manera correcta.

Intenta ejecutarlo una vez más. Y una vez más.

Ahora cierra ambas ventanas ahora y vuelve al escritorio.

 1.2.1.6 Comienza tu viaje en Python

¿Cómo estropear y arreglar tu código?


Ahora ejecuta IDLE otra vez.

Haz clic en File , Open , señala el archivo que guardaste anteriormente y deja que IDLE
lo lea.

Intenta ejecutarlo de nuevo presionando F5  cuando la ventana del editor está activa.

Como puedes ver, IDLE puede guardar tu código y recuperarlo cuando lo necesites de
nuevo.

IDLE contiene una característica adicional y útil.

Primero, quita el paréntesis de cierre.

Luego ingresa el paréntesis nuevamente.

Cada vez que coloques el paréntesis de cierre en tu programa, IDLE mostrará la parte
del texto limitada con un par de paréntesis correspondientes. Esto te ayuda a
recordar colocarlos en pares.

Retira nuevamente el paréntesis de cierre. El código se vuelve erróneo. Ahora contiene


un error de sintaxis. IDLE no debería dejar que lo ejecutes.

Intenta ejecutar el programa de nuevo. IDLE te recordará que guardes el archivo


modificado. Sigue las instrucciones.

 1.2.1.7 Comienza tu viaje en Python


¿Cómo estropear y arreglar tu código?
Mira todas las ventanas con cuidado.

Aparece una nueva ventana: dice que el intérprete ha encontrado un EOF (fin de
archivo).

La ventana del editor muestra claramente donde ocurrió.

Arregla el código ahora. Debe verse así:

print("Hisssssss...")

Ejecútalo para ver si "sisea" de nuevo.

Vamos a arruinar el código una vez más. Elimina una letra de la palabra print. Ejecuta
el código presionando F5. Como puedes ver, Python no puede reconocer el error.

 1.2.1.8 Comienza tu viaje en Python

¿Cómo estropear y arreglar tu código?


Es posible que hayas notado que el mensaje de error generado para el error anterior
es bastante diferente del primero.

Esto se debe a que la naturaleza del error es diferente y el error se descubre en


una etapa diferente de la interpretación.

La ventana del editor no proporcionará ninguna información útil sobre el error, pero
es posible que las ventanas de la consola si.

El mensaje (en rojo) muestra (en las siguientes líneas):

 El rastreo (que es la ruta que el código atraviesa a través de diferentes partes


del programa, puedes ignorarlo por ahora, ya que está vacío en un código tan
simple).
 La ubicación del error (el nombre del archivo que contiene el error, el número
de línea y el nombre del módulo); nota: el número puede ser engañoso, ya que
Python generalmente muestra el lugar donde se da cuenta por primera vez de
los efectos del error, no necesariamente del error en sí.
 El contenido de la línea errónea: nota: la ventana del editor de IDLE no
muestra números de línea, pero muestra la ubicación actual del cursor en la
esquina inferior derecha; utilízalo para ubicar la línea errónea en un código
fuente largo.
 El nombre del error y una breve explicación.

Experimenta creando nuevos archivos y ejecutando tu código. Intenta enviar un


mensaje diferente a la pantalla, por ejemplo,¡rawr!, miau, o incluso tal vez un ¡oink!
Intenta estropear y arreglar tu código, observa que sucede.

 1.3.1.1 Interfaces del curso

Sandbox
Este curso no requiere que instales ninguna aplicación de software para probar tu
código y hacer los ejercicios.

Para probar o experimentar con tu código, puedes utilizar un entorno de


programación en línea interactivo y dedicado.

Sandbox permite que el código Python se ejecute en un navegador de Internet.

Es una herramienta integrada dentro del curso, que se puede usar como un Sandbox
de Python basado en el navegador que te permite probar el código discutido a lo
largo del curso, así como un intérprete que te permite iniciar, realizar y probar los
ejercicios de laboratorio diseñados específicamente para este curso.

La interfaz de Sandbox consta de tres partes principales:

 La ventana del editor que te permite escribir tu código.


 La ventana de consola que te permite ver el resultado de tus programas.
 Una herramienta llamada barra de botones de acción que te permite ejecutar
tu código, actualizar la ventana del editor, descargar tu programa como un
archivo .py, cargar un archivo .py que se mostrará en el editor, informar algún
error (en caso de que detectes uno, ¡háznoslo saber!).
 El botón de Configuración que te permite ajustar la configuración de la
pantalla y cambiar entre los entornos Python / C / C ++.

Ahora copia el siguiente código:

print("Hola!")
print("¡Bienvenido a Fundamentos de Programación en Python!")
print("ESTO ES EL MODO SANDBOX.")
... luego da clic en el botón Sandbox para ingresar al Modo Sandbox, pega el código en
la ventana del editor y haz clic en el botón Ejecutar para ver que sucede.

Para volver a nuestro curso, haz clic en Back to course en la esquina superior derecha
de la interfaz de Sandbox.

 1.3.1.2 Interfaces del curso

Interfaz de práctica
Este curso contiene cuatro tipos diferentes de interfaces.

Hasta ahora, haz visto la Interfaz de estudio (una o dos ventanas con texto e
imágenes/animación) y la Interfaz de Sandbox, que puedes usar para probar tu
propio código (haz clic en Sandbox para cambiar a la Interfaz de Sandbox).

Lo que ves ahora es la Interfaz de práctica, que te permite estudiar cosas nuevas y
realizar tareas de codificación al mismo tiempo. Utilizarás este tipo de interfaz la
mayor parte del tiempo durante el curso.

La Interfaz de práctica consiste en un área de texto a la izquierda y las ventanas del


Editor/Consola a la derecha.

Otro tipo de interfaz que verás en el futuro es la Interfaz de prueba/examen, que te


permitirá verificar tus conocimientos y habilidades para ver que tan bien has
dominado el material de estudio.

# Haz clic en el botón de ejecutar (Run)


print("¡Bienvenido a Fundamentos de Programación en Python!")

 2.1.1.2 Tu primer programa

¡Hola, Mundo!
Es hora de comenzar a escribir código real y funcional en Python. Por el momento
será muy sencillo.

Como se muestran algunos conceptos y términos fundamentales, estos fragmentos de


código no serán complejos ni difíciles.

Ejecuta el código en la ventana del editor a la derecha. Si todo sale bien, veras la línea
de texto en la ventana de consola.
Como alternativa, inicia IDLE, crea un nuevo archivo fuente de Python, coloca este
código, nombra el archivo y guárdalo. Ahora ejecútalo. Si todo sale bien, verás una
línea en la ventana de la consola IDLE. El código que has ejecutado debería parecerte
familiar. Viste algo muy similar cuando te guiamos a través de la configuración del
entorno IDLE.

Ahora dedicaremos un poco de tiempo para mostrarte y explicarte lo que estás viendo
y por que se ve así.

Como puedes ver, el primer programa consta de las siguientes partes:

 La palabra  print .
 Un paréntesis de apertura.
 Una comilla.
 Una línea de texto:  ¡Hola, Mundo! .
 Otra comilla.
 Un paréntesis de cierre.

Cada uno de los elementos anteriores juega un papel muy importante en el código.

print("¡Hola, Mundo!")

 2.1.1.3 Tu primer programa

La función print()
Mira la línea de código a continuación:

print("¡Hola, Mundo!")

La palabra print que puedes ver aquí es el nombre de una función. Eso no significa que
dondequiera que aparezca esta palabra, será siempre el nombre de una función. El significado de la
palabra proviene del contexto en el cual se haya utilizado la palabra.

Probablemente hayas encontrado el término función muchas veces antes, durante las clases de
matemáticas. Probablemente también puedes recordar varios nombres de funciones matemáticas,
como seno o logaritmo.

Las funciones de Python, sin embargo, son más flexibles y pueden contener más contenido que sus
parientes matemáticos.

Una función (en este contexto) es una parte separada del código de computadora el cual es capaz de:

 Causar algún efecto (por ejemplo, enviar texto a la terminal, crear un archivo, dibujar una
imagen, reproducir un sonido, etc.); esto es algo completamente inaudito en el mundo de las
matemáticas.
 Evaluar un valor o algunos valores (por ejemplo, la raíz cuadrada de un valor o la
longitud de un texto dado); esto es lo que hace que las funciones de Python sean parientes
de los conceptos matemáticos.

Además, muchas de las funciones de Python pueden hacer las dos cosas anteriores juntas.

¿De dónde provienen las funciones?

 Pueden venir de Python mismo. La función print es una de este tipo; dicha función es un
valor agregado de Python junto con su entorno (está integrada); no tienes que hacer nada
especial (por ejemplo, pedirle a alguien algo) si quieres usarla.
 Pueden provenir de uno o varios de los módulos de Python llamados complementos;
algunos de los módulos vienen con Python, otros pueden requerir una instalación por
separado, cual sea el caso, todos deben estar conectados explícitamente con el código (te
mostraremos cómo hacer esto pronto).
 Puedes escribirlas tú mismo, colocando tantas funciones como desees y necesites dentro de
su programa para hacerlo más simple, claro y elegante.

El nombre de la función debe ser significativo (el nombre de la función print es evidente), imprime


en la terminal.

Si vas a utilizar alguna función ya existente, no podrás modificar su nombre, pero cuando comiences
a escribir tus propias funciones, debes considerar cuidadosamente la elección de nombres.

 2.1.1.4 Tu primer programa

La función print()
Como se dijo anteriormente, una función puede tener:

 Un efecto.
 Un resultado.

También hay un tercer componente de la función, muy importante, el o los argumento(s).

Las funciones matemáticas usualmente toman un argumento, por ejemplo, sen (x) toma una x, que es
la medida de un ángulo.

Las funciones de Python, por otro lado, son más versátiles. Dependiendo de las necesidades
individuales, pueden aceptar cualquier número de argumentos, tantos como sea necesario para
realizar sus tareas. Nota: algunas funciones de Python no necesitan ningún argumento.

print("¡Hola, Mundo!")

A pesar del número de argumentos necesarios o proporcionados, las funciones de Python demandan
fuertemente la presencia de un par de paréntesis - el de apertura y de cierre, respectivamente.
Si deseas entregar uno o más argumentos a una función, colócalos dentro de los paréntesis. Si vas a
utilizar una función que no tiene ningún argumento, aún tiene que tener los paréntesis.

Nota: para distinguir las palabras comunes de los nombres de funciones, coloca un par de
paréntesis vacíos después de sus nombres, incluso si la función correspondiente requiere uno o más
argumentos. Esta es una medida estándar.

La función de la que estamos hablando aquí es  print() .

¿La función  print()  en nuestro ejemplo tiene algún argumento?

Por supuesto que si, pero ¿Qué son los argumentos?

 2.1.1.5 Tu primer programa

La función print()
El único argumento entregado a la función  print()  en este ejemplo es una cadena:

print("¡Hola, Mundo!")

Como se puede ver, la cadena está delimitada por comillas - de hecho, las comillas
forman la cadena, recortan una parte del código y le asignan un significado diferente.

Podemos imaginar que las comillas significan algo así: el texto entre nosotros no es un
código. No está diseñado para ser ejecutado, y se debe tomar tal como está.

Casi cualquier cosa que ponga dentro de las comillas se tomará de manera literal, no
como código, sino como datos. Intenta jugar con esta cadena en particular - puedes
modificarla. Ingresa contenido nuevo o borra parte del contenido existente.

Existe más de una forma de como especificar una cadena dentro del código de Python,
pero por ahora, esta será suficiente.

Hasta ahora, has aprendido acerca de dos partes importantes del código- la función y
la cadena. Hemos hablado de ellos en términos de sintaxis, pero ahora es el momento
de discutirlos en términos de semántica.

 2.1.1.6 Tu primer programa

La función print()
El nombre de la función (print en este caso) junto con los paréntesis y los argumentos, forman
la invocación de la función.

Discutiremos esto en mayor profundidad mas adelante, pero por lo pronto, arrojaremos un poco más
de luz al asunto.

print("¡Hola, Mundo!")

¿Qué sucede cuando Python encuentra una invocación como la que está a continuación?

nombreFunción(argumento)

Veamos:

 Primero, Python comprueba si el nombre especificado es legal (explora sus datos internos


para encontrar una función existente del nombre; si esta búsqueda falla, Python cancela el
código).
 En segundo lugar, Python comprueba si los requisitos de la función para el número de
argumentos le permiten invocar la función de esta manera (por ejemplo, si una función
específica exige exactamente dos argumentos, cualquier invocación que entregue solo un
argumento se considerará errónea y abortará la ejecución del código).
 Tercero, Python deja el código por un momento y salta dentro de la función que se desea
invocar; por lo tanto, también toma los argumentos y los pasa a la función.
 Cuarto, la función ejecuta el código, provoca el efecto deseado (si lo hubiera), evalúa el
(los) resultado(s) deseado(s) y termina la tarea.
 Finalmente, Python regresa al código (al lugar inmediatamente después de la invocación) y
reanuda su ejecución.

 2.1.1.7 LABORATORIO: La función print()

LABORATORIO

Tiempo Estimado
5 minutos

Nivel de dificultad
Muy fácil

Objetivos
 Familiarizarse con la función  print()  y sus capacidades de formateo.
 Experimentar con el código de Python.

Escenario
El comando  print()  , el cual es una de las directivas más sencillas de Python,
simplemente imprime una línea de texto en la pantalla.

En tu primer laboratorio:

 Utiliza la función  print()  para imprimir la linea  "¡Hola, Mundo!"  en la


pantalla.
 Una vez hecho esto, utiliza la función  print()  nuevamente, pero esta vez
imprime tu nombre.
 Elimina las comillas dobles y ejecuta el código. Observa la reacción de Python.
¿Qué tipo de error se produce?
 Luego, elimina los paréntesis, vuelve a poner las comillas dobles y vuelve a
ejecutar el código. ¿Qué tipo de error se produce esta vez?
 Experimenta tanto como puedas. Cambia las comillas dobles a comillas
simples, utiliza múltiples funciones  print()  en la misma línea y luego en líneas
diferentes. Observa que es lo que ocurre.

Print (“ ¡Hola, Mundo! “)

 2.1.1.8 Tu primer programa

La función print()

Tres preguntas importantes deben ser respondidas antes de continuar:

1. ¿Cuál es el efecto que causa la función  print() ?

El efecto es muy útil y espectacular. La función toma los argumentos (puede aceptar más de un
argumento y también puede aceptar menos de un argumento) los convierte en un formato legible
para el ser humano si es necesario (como puedes sospechar, las cadenas no requieren esta acción, ya
que la cadena ya está legible) y envía los datos resultantes al dispositivo de salida (generalmente
la consola); en otras palabras, cualquier cosa que se ponga en la función de print() aparecerá en la
pantalla.

No es de extrañar entonces, que de ahora en adelante, utilizarás  print()  muy intensamente para
ver los resultados de tus operaciones y evaluaciones.
2. ¿Qué argumentos espera  print() ?

Cualquiera. Te mostraremos pronto que  print()  puede operar con prácticamente todos los tipos
de datos ofrecidos por Python. Cadenas, números, caracteres, valores lógicos, objetos: cualquiera de
estos se puede pasar con éxito a  print() .

3. ¿Qué valor evalúa la función  print() ?

Ninguno. Su efecto es suficiente -  print()  no evalúa nada.

 2.1.1.9 Tu primer programa

La función print() - instrucciones
A estas alturas ya sabes que este programa contiene una invocación de función. A su
vez, la invocación de función es uno de los posibles tipos de instrucciones de Python.
Por lo tanto, este programa consiste de una sola instrucción.

Por supuesto, cualquier programa complejo generalmente contiene muchas más


instrucciones que una. La pregunta es, ¿Cómo se acopla más de una instrucción en el
código de Python?

La sintaxis de Python es bastante específica en esta área. A diferencia de la mayoría de


los lenguajes de programación, Python requiere que no haya más de una instrucción
por una línea.

Una línea puede estar vacía (por ejemplo, puede no contener ninguna instrucción)
pero no debe contener dos, tres o más instrucciones. Esto está estrictamente
prohibido.

Nota: Python hace una excepción a esta regla: permite que una instrucción se extienda
por más de una línea (lo que puede ser útil cuando el código contiene construcciones
complejas).

Vamos a expandir el código un poco, puedes verlo en el editor. Ejecutalo y nota lo que
ves en la consola.

print("La Witsi Witsi Araña subió a su telaraña.")

print("Vino la lluvia y se la llevó.")


Tu consola Python ahora debería verse así:

La Witsi Witsi Araña subió a su telaraña.

Vino la lluvia y se la llevó.

Esta es una buena oportunidad para hacer algunas observaciones:

 El programa invoca la función  print()  dos veces, como puedes ver hay dos
líneas separadas en la consola: esto significa que  print()  comienza su salida
desde una nueva línea cada vez que comienza su ejecución. Puedes cambiar
este comportamiento, pero también puedes usarlo a tu favor.
 Cada invocación de  print()  contiene una cadena diferente, como su
argumento y el contenido de la consola lo reflejan- esto significa que las
instrucciones en el código se ejecutan en el mismo orden en que se
colocaron en el archivo de origen; no se ejecuta la siguiente instrucción hasta
que se complete la anterior (hay algunas excepciones a esta regla, pero puedes
ignorarlas por ahora).

 2.1.1.10 Tu primer programa

La función print() - instrucciones

Hemos cambiado un poco el ejemplo: hemos agregado una invocación vacía de la


función  print() . La llamamos vacía porque no hemos agregado ningún argumento a
la función.

Lo puedes ver en la ventana del editor. Ejecuta el código.

¿Qué ocurre?

Si todo sale bien, deberías ver algo como esto:

La Witsi Witsi Araña subió a su telaraña.


Vino la lluvia y se la llevó.

Como puedes ver, la invocación de  print()  vacía no esta tan vacía como se esperaba
- genera una línea vacía (esta interpretación también es correcta) su salida es solo una
nueva línea.

Esta no es la única forma de producir una nueva linea en la consola de salida.


Enseguida mostraremos otra manera.

print("La Witsi Witsi Araña subió a su telaraña.")


print()
print("Vino la lluvia y se la llevó.")

 2.1.1.11 Tu primer programa

La función print() - los caracteres de escape y


nueva línea
Hemos modificado el código de nuevo. Obsérvalo con cuidado.

Hay dos cambios muy sutiles: hemos insertado un par extraño de caracteres dentro
del texto. Se ven así:  \n .

Curiosamente, mientras tu ves dos caracteres, Python ve solo uno.

La barra invertida ( \ ) tiene un significado muy especial cuando se usa dentro de las
cadenas, es llamado el carácter de escape.

La palabra escape debe entenderse claramente- significa que la serie de caracteres en


la cadena se escapa (detiene) por un momento (un momento muy corto) para
introducir una inclusión especial.

En otras palabras, la barra invertida no significa nada, sino que es solo un tipo de
anuncio, de que el siguiente carácter después de la barra invertida también tiene un
significado diferente.

La letra  n  colocada después de la barra invertida proviene de la palabra newline (nueva


linea).

Tanto la barra diagonal inversa como la n forman un símbolo especial


denominado carácter de nueva línea (newline character), que incita a la consola a
iniciar una nueva línea de salida.
Ejecuta el código. La consola ahora debería verse así:

La Witsi Witsi Araña


subió a su telaraña.

Vino la lluvia
y se la llevó.

Como se puede observar, aparecen dos nuevas líneas en la canción infantil, en los
lugares donde se ha utilizado  \n .

print("La Witsi Witsi Araña\nsubió a su telaraña.\n")

print()

print("Vino la lluvia\ny se la llevó.")

 2.1.1.12 Tu primer programa

La función print() los caracteres de escape y nueva


línea
El utilizar la diagonal invertida tiene dos características importantes:

1. Si deseas colocar solo una barra invertida dentro de una cadena, no olvides su
naturaleza de escape: tienes que duplicarla, por ejemplo, la siguiente invocación
causará un error:

print("\")

Mientras que esta no lo hará:

print("\\") muestra \ en otra linea

2. No todos los pares de escape (la barra invertida junto con otro carácter) significan
algo.
Experimenta con el código en el editor, ejecútalo y observa lo que sucede.

print("La Witsi Witsi Araña\nsubió a su telaraña.")


print()
print("Vino la lluvia\ny se la llevó.")

 2.1.1.13 Tu primer programa

La función print() utilizando argumentos múltiples


Hasta ahora se ha probado el comportamiento de la función  print()  sin argumentos
y con un argumento. También vale la pena intentar alimentar la función print() con
más de un argumento.

Mira la ventana del editor. Esto es lo que vamos a probar ahora:

print("Witsi witsi araña" , "subió" , "su telaraña.")

Hay una invocación de la función  print()  pero contiene tres argumentos. Todos


ellos son cadenas.

Los argumentos están separados por comas. Se han rodeado de espacios para


hacerlos más visibles, pero no es realmente necesario y no se hará más.

En este caso, las comas que separan los argumentos desempeñan un papel
completamente diferente a la coma dentro de la cadena. El primero es una parte de la
sintaxis de Python, el segundo está destinado a mostrarse en la consola.

Si vuelves a mirar el código, verás que no hay espacios dentro de las cadenas.

Ejecuta el código y observa lo que pasa.

La consola ahora debería mostrar el siguiente texto:

La Witsi witsi araña subió su telaraña.

Los espacios, removidos de las cadenas, han vuelto a aparecer. ¿Puedes explicar
porque?

Dos conclusiones surgen de este ejemplo:


 Una función  print()  invocada con más de un argumento genera la salida en
una sola línea.
 La función  print()  pone un espacio entre los argumentos emitidos por
iniciativa propia.

print("La Witsi Witsi Arañar" , "subió" , "a su telaraña.")

 2.1.1.14 Tu primer programa

La función print() - La manera posicional de pasar


los argumentos
Ahora que sabes un poco acerca de la función  print()  y como personalizarla, te
mostraremos como cambiarla.

Deberías de poder predecir la salida sin ejecutar el código en el editor.

La forma en que pasamos los argumentos a la función  print()  es la más común en
Python, y se denomina manera posicional (este nombre proviene del hecho de que el
significado del argumento está dictado por su posición, por ejemplo, el segundo
argumento se emitirá después del primero, y no al revés).

Ejecuta el código y verifica si la salida coincide con tus predicciones.

print("Mi nombre es", "Python.")


print("Monty Python.")

 2.1.1.15 Tu primer programa

La función print() - los argumentos de palabras


clave
Python ofrece otro mecanismo para transmitir o pasar los argumentos, que puede ser
útil cuando se desea convencer a la función  print()  de que cambie su
comportamiento un poco.

No se va a explicar en profundidad ahora. Se planea hacer esto cuando se trate el


tema de funciones. Por ahora, simplemente queremos mostrarte como funciona.
Siéntete libre de utilizarlo en tus propios programas.

El mecanismo se llama argumentos de palabras clave. El nombre se deriva del hecho


de que el significado de estos argumentos no se toma de su ubicación (posición) sino
de la palabra especial (palabra clave) utilizada para identificarlos.
La función  print()  tiene dos argumentos de palabras clave que se pueden utilizar
para estos propósitos. El primero de ellos se llama  end .

En la ventana del editor se puede ver un ejemplo muy simple de como utilizar un
argumento de palabra clave.

Para utilizarlo es necesario conocer algunas reglas:

 Un argumento de palabra clave consta de tres elementos: una palabra


clave que identifica el argumento ( end  -termina aquí); un signo de igual ( = ); y
un valor asignado a ese argumento.
 Cualquier argumento de palabra clave debe ponerse después del último
argumento posicional (esto es muy importante).

En nuestro ejemplo, hemos utilizado el argumento de palabra clave  end  y lo hemos


igualado a una cadena que contiene un espacio.

Ejecuta el código para ver como funciona.

La consola ahora debería mostrar el siguiente texto:

Mi nombre es Python. Monty Python.

Como puedes ver, el argumento de palabra clave  end  determina los caracteres que la
función print() envía a la salida una vez que llega al final de sus argumentos
posicionales.

El comportamiento predeterminado refleja la situación en la que el argumento de la


palabra clave  end  se usa implícitamente de la siguiente manera:  end="\n" .

print("Mi nombre es", "Python.", end=" ")


print("Monty Python.")

 2.1.1.16 Tu primer programa

La función print() - los argumentos de palabras


clave
Y ahora, es el momento de intentar algo más difícil.
Si observas detenidamente, verás que hemos utilizado el argumento  end  , pero su
cadena asignada está vacía (no contiene ningún carácter).

¿Qué pasará ahora? Ejecuta el programa en el editor para averiguarlo.

Ya que al argumento  end  se le ha asignado a nada, la función  print()  tampoco


genera nada, una vez que se hayan agotado los argumentos posicionales.

La consola ahora debería mostrar el siguiente texto:

Mi nombre es Monty Python.

Nota: No se han enviado nuevas líneas a la salida..

La cadena asignada al argumento de la palabra clave  end  puede ser de cualquier


longitud. Experimenta con ello si gustas.

print("Mi nombre es ", end="")


print("Monty Python.")

 2.1.1.17 Tu primer programa

La función print() - los argumentos de palabras


clave
Se estableció anteriormente que la función  print()  separa los argumentos
generados con espacios. Este comportamiento también puede ser cambiado.

El argumento de palabra clave que puede hacer esto se


denomina  sep  (como separador).

Mira el código en el editor y ejecútalo.

El argumento  sep  entrega el siguiente resultado:

Mi-nombre-es-Monty-Python.

La función  print()  ahora utiliza un guión, en lugar de un espacio, para separar los
argumentos generados.
Nota: el valor del argumento  sep  también puede ser una cadena vacía. Pruébalo tu
mismo.

print("Mi", "nombre", "es", "Monty", "Python.", sep="-")

 2.1.1.18 Tu primer programa

La función print() - los argumentos de palabras


clave
Ambos argumentos de palabras clave pueden mezclarse en una invocación, como
aquí en la ventana del editor.

El ejemplo no tiene mucho sentido, pero representa visiblemente las interacciones


entre  end  y  sep .

¿Puedes predecir la salida?

Ejecuta el código y ve si coincide con tus predicciones.

Ahora que comprendes la función  print()  , estás listo para considerar aprender
cómo almacenar y procesar datos en Python.

Sin  print() , no se podría ver ningún resultado.

print("Mi", "nombre", "es", sep="_", end="*")


print("Monty", "Python.", sep="*", end="*\n")

Mi_nombre_es*Monty*Python.*

 2.1.1.19 LABORATORIO: La función print()

LABORATORIO

Tiempo Estimado
5 minutos

Nivel de dificultad
Muy fácil
Objetivos
 Familiarizarse con la función de  print()  y sus capacidades de formato.
 Experimentar con el código de Python.

Escenario
Modifica la primera línea de código en el editor, utilizando las palabras
clave  sep  y  end , para que coincida con el resultado esperado. Recuerda, utilizar dos
funciones  print() .

No cambies nada en la segunda invocación de  print() .

Resultado Esperado
Fundamentos***Programación***en...Python

print("Fundamentos","Programación","en", sep=”***”,end=”…”)
print("Python")

 2.1.1.20 LABORATORIO: Dando formato a la salida

LABORATORIO

Tiempo Estimado
5-10 minutos

Nivel de dificultad
Facil

Objetivos
 Experimentar con el código Python existente.
 Descubrir y solucionar errores básicos de sintaxis.
 Familiarizarse con la función  print()  y sus capacidades de formato.
Escenario
Recomendamos que juegues con el código que hemos escrito para ti y que realices algunas
correcciones (quizás incluso destructivas). Siéntete libre de modificar cualquier parte del código,
pero hay una condición: aprende de tus errores y saca tus propias conclusiones.

Intenta:

 Minimizar el número de invocaciones de la función  print()  insertando la secuencia  \


n  en las cadenas.
 Hacer la flecha dos veces más grande (pero mantener las proporciones).
 Duplicar la flecha, colocando ambas flechas lado a lado; nota: una cadena se puede
multiplicar usando el siguiente truco:  "string" * 2  producirá  "stringstring"  (te
contaremos más sobre ello pronto).
 Elimina cualquiera de las comillas y observa detenidamente la respuesta de Python; presta
atención a donde Python ve un error: ¿es el lugar en donde realmente existe el error?
 Haz lo mismo con algunos de los paréntesis.
 Cambia cualquiera de las palabras  print  en otra cosa (por ejemplo de minuscula a
mayuscula,  Print ) - ¿Qué sucede ahora?
 Reemplaza algunas de las comillas por apóstrofes; observa lo que pasa detenidamente.

print(" *")
print(" * *")
print(" * *")
print(" * *")
print("*** ***")
print(" * *")
print(" * *")
print(" *****")
 2.1.2.1 Literales de Python

Literales - los datos en si mismos


Ahora que tienes un poco de conocimiento acerca de algunas de las poderosas características que
ofrece la función  print() , es tiempo de aprender sobre cuestiones nuevas, y un nuevo término -
el literal.

Un literal se refiere a datos cuyos valores están determinados por el literal mismo.

Debido a que es un concepto un poco difícil de entender, un buen ejemplo puede ser muy útil.

Observa los siguientes dígitos:

123

¿Puedes adivinar qué valor representa? claro que puedes - es ciento veintitrés.
Que tal este:

¿Representa algún valor? Tal vez. Puede ser el símbolo de la velocidad de la luz, por ejemplo.
También puede representar la constante de integración. Incluso la longitud de una hipotenusa en el
Teorema de Pitágoras. Existen muchas posibilidades.

No se puede elegir el valor correcto sin algo de conocimiento adicional.

Y esta es la pista:  123  es un literal, y  c  no lo es.

Se utilizan literales para codificar datos y ponerlos dentro del código. Ahora mostraremos algunas
convenciones que se deben seguir al utilizar Python.

print("2")
print(2)

Enteros
Quizá ya sepas un poco acerca de como las computadoras hacen cálculos con números. Tal vez has
escuchado del sistema binario, y como es que ese es el sistema que las computadoras utilizan para
almacenar números y como es que pueden realizar cualquier tipo de operaciones con ellos.

No exploraremos las complejidades de los sistemas numéricos posicionales, pero se puede afirmar
que todos los números manejados por las computadoras modernas son de dos tipos:

 Enteros, es decir, aquellos que no tienen una parte fraccionaria.


 Y números punto-flotantes (o simplemente flotantes), los cuales contienen (o son capaces
de contener) una parte fraccionaría.

Esta definición no es tan precisa, pero es suficiente por ahora. La distinción es muy importante, y la
frontera entre estos dos tipos de números es muy estricta. Ambos tipos difieren significativamente en
como son almacenados en una computadora y en el rango de valores que aceptan.

La característica del valor numérico que determina el tipo, rango y aplicación se denomina el tipo.

Si se codifica un literal y se coloca dentro del código de Python, la forma del literal determina la
representación (tipo) que Python utilizará para almacenarlo en la memoria.

Por ahora, dejemos los números flotantes a un lado (regresaremos a ellos pronto) y analicemos como
es que Python reconoce un numero entero.

El proceso es casi como usar lápiz y papel, es simplemente una cadena de dígitos que conforman el
número, pero hay una condición, no se deben insertar caracteres que no sean dígitos dentro del
número.
Tomemos por ejemplo, el número once millones ciento once mil ciento once. Si tomaras ahorita un
lápiz en tu mano, escribirías el siguiente numero:  11,111,111 , o así:  11.111.111 , incluso de
esta manera:  11 111 111 .

Es claro que la separación hace que sea más fácil de leer, especialmente cuando el número tiene
demasiados dígitos. Sin embargo, Python no acepta estas cosas. Esta prohibido. ¿Qué es lo que
Python permite? El uso de guion bajo en los literales numéricos.*

Por lo tanto, el número se puede escribir ya sea así:  11111111 , o como sigue:  11_111_111 .

NOTA   *Python 3.6 ha introducido el guion bajo en los literales numéricos, permitiendo colocar un
guion bajo entre dígitos y después de especificadores de base para mejorar la legibilidad. Esta
característica no está disponible en versiones anteriores de Python.

¿Cómo se codifican los números negativos en Python? Como normalmente se hace, agregando un
signo de menos. Se puede escribir:  -11111111 , o  -11_111_111 .

Los números positivos no requieren un signo positivo antepuesto, pero es permitido, si se desea
hacer. Las siguientes líneas describen el mismo número:  +11111111  y  11111111 .

Enteros: números octales y hexadecimales


Existen dos convenciones adicionales en Python que no son conocidas en el mundo de
las matemáticas. El primero nos permite utilizar un número en su
representación octal.

Si un numero entero esta precedido por un código  0O  o  0o  (cero-o), el numero será
tratado como un valor octal. Esto significa que el número debe contener dígitos en el
rango del [0..7] únicamente.

0o123  es un número octal con un valor (decimal) igual a  83 .

La función  print()  realiza la conversión automáticamente. Intenta esto:

print(0o123)

La segunda convención nos permite utilizar números en hexadecimal. Dichos


números deben ser precedidos por el prefijo  0x  o  0X  (cero-x).
0x123  es un número hexadecimal con un valor (decimal) igual a  291 . La función
print() puede manejar estos valores también. Intenta esto:

print(0x123)

Flotantes
Ahora es tiempo de hablar acerca de otro tipo, el cual esta designado para representar y almacenar
los números que (como lo diría un matemático) tienen una parte decimal no vacía.

Son números que tienen (o pueden tener) una parte fraccionaria después del punto decimal, y aunque
esta definición es muy pobre, es suficiente para lo que se desea discutir.

Cuando se usan términos como dos y medio o menos cero punto cuatro, pensamos en números que la
computadora considera como números punto-flotante:

2.5
-0.4

Nota: dos punto cinco se ve normal cuando se escribe en un programa, sin embargo si tu idioma
nativo prefiere el uso de una coma en lugar de un punto, se debe asegurar que el número no
contenga más comas.

Python no lo aceptará, o (en casos poco probables) puede malinterpretar el número, debido a que la
coma tiene su propio significado en Python.

Si se quiere utilizar solo el valor de dos punto cinco, se debe escribir como se mostró anteriormente.
Nota que hay un punto entre el 2 y el 5 - no una coma.

Como puedes imaginar, el valor de cero punto cuatro puede ser escrito en Python como:

0.4

Pero no hay que olvidar esta sencilla regla, se puede omitir el cero cuando es el único dígito antes
del punto decimal.

En esencia, el valor  0.4  se puede escribir como:

.4

Por ejemplo: el valor de  4.0  puede ser escrito como:

4.

Esto no cambiará su tipo ni su valor.


Enteros vs. Flotantes
El punto decimal es esencialmente importante para reconocer números punto-flotantes en Python.

Observa estos dos números:

4.0

Se puede pensar que son idénticos, pero Python los ve de una manera completamente distinta.

4  es un número entero, mientras que  4.0  es un número punto-flotante .

El punto decimal es lo que determina si es flotante.

Por otro lado, no solo el punto hace que un número sea flotante. Se puede utilizar la letra  e .

Cuando se desea utilizar números que son muy pequeños o muy grandes, se puede implementar
la notación científica.

Por ejemplo, la velocidad de la luz, expresada en metros por segundo. Escrita directamente se vería
de la siguiente manera:  300000000 .

Para evitar escribir tantos ceros, los libros de texto emplean la forma abreviada, la cual
probablemente hayas visto:  3 x 108 .

Se lee de la siguiente manera: tres por diez elevado a la octava potencia.

En Python, el mismo efecto puede ser logrado de una manera similar, observa lo siguiente:

3E8

La letra  E  (también se puede utilizar la letra minúscula  e  - proviene de la palabra exponente) la cual


significa por diez a la n potencia.

Nota:

 El exponente (el valor después de la E) debe ser un valor entero.


 La base (el valor antes de la E) puede o no ser un valor entero.

Codificando Flotantes
Veamos ahora como almacenar números que son muy pequeños (en el sentido de que están muy
cerca del cero).

Una constante de física denominada "La Constante de Planck"  (denotada como h), de acuerdo con
los libros de texto, tiene un valor de: 6.62607 x 10-34.

Si se quisiera utilizar en un programa, se debería escribir de la siguiente manera:

6.62607E-34

Nota: el hecho de que se haya escogido una de las posibles formas de codificación de un valor
flotante no significa que Python lo presentará de la misma manera.

Python podría en ocasiones elegir una notación diferente.

Por ejemplo, supongamos que se ha elegido utilizar la siguiente notación:

0.0000000000000000000001

Cuando se corre en Python:

print(0.0000000000000000000001)

Este es el resultado:

1e-22

salida

Python siempre elige la presentación más corta del número, y esto se debe de tomar en
consideración al crear literales.

Cadenas
Las cadenas se emplean cuando se requiere procesar texto (como nombres de cualquier tipo,
direcciones, novelas, etc.), no números.

Ya conoces un poco acerca de ellos, por ejemplo, que las cadenas requieren comillas así como los
flotantes necesitan punto decimal.

Este es un ejemplo de una cadena:  "Yo soy una cadena."

Sin embargo, hay una cuestión. ¿Cómo se puede codificar una comilla dentro de una cadena que ya
está delimitada por comillas?
Supongamos que se desea mostrar un muy sencillo mensaje:

Me gusta "Monty Python"

¿Cómo se puede hacer esto sin generar un error? Existen dos posibles soluciones.

La primera se basa en el concepto ya conocido del carácter de escape, el cual recordarás se utiliza


empleando la diagonal invertida. La diagonal invertida puede también escapar de la comilla. Una
comilla precedida por una diagonal invertida cambia su significado, no es un limitador, simplemente
es una comilla. Lo siguiente funcionará como se desea:

print("Me gusta \"Monty Python\"")

Nota: ¿Existen dos comillas con escape en la cadena, puedes observar ambas?

La segunda solución puede ser un poco sorprendente. Python puede utilizar una apóstrofe en lugar
de una comilla. Cualquiera de estos dos caracteres puede delimitar una cadena, pero para ello se
debe ser consistente.

Si se delimita una cadena con una comilla, se debe cerrar con una comilla.

Si se inicia una cadena con un apóstrofe, se debe terminar con un apóstrofe.

Este ejemplo funcionará también:

print('Me gusta "Monty Python"')

Nota: en este ejemplo no se requiere nada de escapes.

Codificando cadenas
Ahora, la siguiente pregunta es: ¿Cómo se puede insertar un apóstrofe en una cadena
la cual está limitada por dos apóstrofes?

A estas alturas ya se debería tener una posible respuesta o dos.

Intenta imprimir una cadena que contenga el siguiente mensaje:

I'm Monty Python.

¿Sabes cómo hacerlo? Haz clic en Revisar para saber si estas en lo cierto:

Revisar
print('I\'m Monty Python.')
o

print("I'm Monty Python.")

Como se puede observar, la diagonal invertida es una herramienta muy poderosa,


puede escapar no solo comillas, sino también apóstrofes.

Ya se ha mostrado, pero se desea hacer énfasis en este fenómeno una vez mas - una
cadena puede estar vacía - puede no contener caracter alguno.

Una cadena vacía sigue siendo una cadena:

''
""

Valores Booleanos
Para concluir con los literales de Python, existen dos más.

No son tan obvios como los anteriores y se emplean para representar un valor muy abstracto - la
veracidad.

Cada vez que se le pregunta a Python si un número es más grande que otro, el resultado es la
creación de un tipo de dato muy específico - un valor booleano.

El nombre proviene de George Boole (1815-1864), el autor de Las Leyes del Pensamiento, las cuales
definen el Algebra Booleana - una parte del algebra que hace uso de dos
valores:  Verdadero  y  Falso , denotados como  1  y  0 .

Un programador escribe un programa, y el programa hace preguntas. Python ejecuta el programa, y


provee las respuestas. El programa debe ser capaz de reaccionar acorde a las respuestas recibidas.

Afortunadamente, las computadoras solo conocen dos tipos de respuestas:

 Si, esto es verdad.


 No, esto es falso.

Nunca habrá una respuesta como: No lo sé o probablemente si, pero no estoy seguro.

Python, es entonces, un reptil binario.

Estos dos valores booleanos tienen denotaciones estrictas en Python:

True
False

No se pueden cambiar, se deben tomar estos símbolos como son, incluso respetando las mayúsculas
y minúsculas.

Reto: ¿Cuál será el resultado del siguiente fragmento de código?

print(True > False)

print(True < False)

Ejecuta el código en la terminal. ¿Puedes explicar el resultado?

LABORATORIO

Tiempo Estimado
5 minutos

Nivel de dificultad
Fácil

Objetivos
 Familiarizarse con la función  print()  y sus capacidades de formato.
 Practicar el codificar cadenas.
 Experimentar con el código de Python.

Escenario
Escribe una sola línea de código, utilizando la función  print() , así como los
caracteres de nueva línea y escape, para obtener la salida esperada de tres líneas.
Salida Esperada
"Estoy"

""aprendiendo""

"""Python"""

Print(‘\”Estoy\” \n\””Aprendiendo”” \n\”””Python”””’)

 2.1.2.12 RESUMEN DE LA SECCIÓN

Puntos Clave
1. Literales son notaciones para representar valores fijos en el código. Python tiene varios tipos de
literales, es decir, un literal puede ser un número por ejemplo,  123 ), o una cadena (por ejemplo, "Yo
soy un literal.").

2. El Sistema Binario es un sistema numérico que emplea 2 como su base. Por lo tanto, un número
binario está compuesto por 0s y 1s únicamente, por ejemplo,  1010  es 10 en decimal.

Los sistemas de numeración Octales y Hexadecimales son similares pues emplean 8 y 16 como sus
bases respectivamente. El sistema hexadecimal utiliza los números decimales más seis letras
adicionales.

3. Los Enteros (o simplemente int) son uno de los tipos numéricos que soporta Python. Son
números que no tienen una parte fraccionaria, por ejemplo,  256 , o  -1  (enteros negativos).

4. Los números Punto-Flotante (o simplemente flotantes) son otro tipo numérico que soporta


Python. Son números que contienen (o son capaces de contener) una parte fraccionaria, por
ejemplo,  1.27 .

5. Para codificar un apóstrofe o una comilla dentro de una cadena se puede utilizar el carácter de
escape, por ejemplo,  'I\'m happy.' , o abrir y cerrar la cadena utilizando un conjunto de
símbolos distintos al símbolo que se desea codificar, por ejemplo,  "I'm happy."  para codificar
un apóstrofe, y  'Él dijo "Python", no "typhoon"'  para codificar comillas.

6. Los Valores Booleanos son dos objetos constantes  Verdadero  y  Falso  empleados para
representar valores de verdad (en contextos numéricos  1  es  True , mientras que  0  es  False ).

EXTRA

Existe un literal especial más utilizado en Python: el literal  None . Este literal es llamado un objeto
de  NonType  (ningún tipo), y puede ser utilizado para representar la ausencia de un valor. Pronto
se hablará más acerca de ello.
Ejercicio 1

¿Qué tipos de literales son los siguientes dos ejemplos?

"Hola", "007"
Revisar

Ambos son cadenas.

Ejercicio 2

¿Qué tipo de literales son los siguientes cuatro ejemplos?

"1.5", 2.0, 528, False


Revisar

El primero es una cadena, el segundo es numérico (flotante), el tercero es numérico (entero) y el


cuarto es booleano.

Ejercicio 3

¿Cuál es el valor en decimal del siguiente numero en binario?

1011
Revisar

Es  11 , porque (2**0) + (2**1) + (2**3) = 11

 2.1.3.1 Operadores, herramientas para la manipulación de datos

Python como una calculadora


Ahora, se va a mostrar un nuevo lado de la función print(). Ya se sabe que la función es
capaz de mostrar los valores de los literales que le son pasados por los argumentos.

De hecho, puede hacer algo más. Observa el siguiente fragmento de código:

print(2+2) salida 4

Reescribe el código en el editor y ejecútalo. ¿Puedes adivinar la salida?


Deberías de ver el número cuatro. Tómate la libertad de experimentar con otros
operadores.

Sin tomar esto con mucha seriedad, has descubierto que Python puede ser utilizado
como una calculadora. No una muy útil, y definitivamente no una de bolsillo, pero una
calculadora sin duda alguna.

Tomando esto más seriamente, nos estamos adentrado en el terreno de


los operadores y expresiones.

Los Operadores Básicos


Un operador es un símbolo del lenguaje de programación, el cual es capaz de realizar
operaciones con los valores.

Por ejemplo, como en la aritmética, el signo de  +  (mas) es un operador el cual es capaz
de sumar dos numeros, dando el resultado de la suma.

Sin embargo, no todos los operadores de Python son tan simples como el signo de
mas, veamos algunos de los operadores disponibles en Python, las reglas que se
deben seguir para emplearlos, y como interpretar las reglas que realizan.

Se comenzará con los operadores que están asociados con las operaciones aritméticas
más conocidas:

+ ,  - ,  * ,  / ,  // ,  % ,  **

El orden en el que aparecen no es por casualidad. Hablaremos más de ello cuando se


hayan visto todos.

Recuerda: Cuando los datos y operadores se unen, forman juntos expresiones. La


expresión más sencilla es el literal.

Operadores aritméticos: exponenciación


Un signo de  **  (doble asterisco) es un operador de exponenciación (potencia). El
argumento a la izquierda es la base, el de la derecha, el exponente.

Las matemáticas clásicas prefieren una notación con superíndices, como el


siguiente: 23. Los editores de texto puros no aceptan esa notación, por lo tanto Python
utiliza  **  en lugar de la notación matemática, por ejemplo,  2 ** 3 .
Observa los ejemplos en la ventana del editor.

print(2 ** 3) salida 8

print(2 ** 3.) salida 8.0

print(2. ** 3) salida 8.0

print(2. ** 3.) salida 8.0

Nota: En los ejemplos, los dobles asteriscos están rodeados de espacios, no es


obligatorio hacerlo pero hace que el código sea mas legible.

Los ejemplos muestran una característica importante de los operadores


numéricos de Python.

Ejecuta el código y observa cuidadosamente los resultados que arroja. ¿Puedes


observar algo?

Recuerda: Es posible formular las siguientes reglas con base en los resultados:

 Cuando ambos  **  argumentos son enteros, el resultado es entero también.


 Cuando al menos un  **  argumento es flotante, el resultado también es
flotante.

Esta es una distinción importante que se debe recordar.

Operadores aritméticos: multiplicación


Un símbolo de  *  (asterisco) es un operador de multiplicación.

Ejecuta el código y revisa si la regla de entero vs flotante aún funciona.

print(2 * 3) salida 6

print(2 * 3.) salida 6.0

print(2. * 3) salida 6.0

print(2. * 3.) salida 6.0

Operadores aritméticos: división


Un símbolo de  /  (diagonal) es un operador de división.

El valor después de la diagonal es el dividendo, el valor antes de la diagonal es


el divisor.

Ejecuta el código y analiza los resultados.

print(6 / 3) salida 2.0


print(6 / 3.) salida 2.0
print(6. / 3) salida 2.0
print(6. / 3.) salida 2.0

Deberías de poder observar que hay una excepción a la regla.

El resultado producido por el operador de división siempre es flotante, sin


importar si a primera vista el resultado es flotante:  1 / 2 , o si parece ser
completamente entero:  2 / 1 .

¿Esto ocasiona un problema? Sí, en ocasiones se podrá necesitar que el resultado de


una división sea entero, no flotante.

Afortunadamente, Python puede ayudar con eso.

Operadores aritméticos: división entera


Un símbolo de  //  (doble diagonal) es un operador de división entera. Difiere del operador
estándar  /  en dos detalles:

 El resultado carece de la parte fraccionaria, está ausente (para los enteros), o siempre es
igual a cero (para los flotantes); esto significa que los resultados siempre son
redondeados.
 Se ajusta a la regla entero vs flotante.

Ejecuta el ejemplo debajo y observa los resultados:

print(6 // 3) salida 2

print(6 // 3.) salida 2.0

print(6. // 3) salida 2.0

print(6. // 3.) salida2.0

Como se puede observar, una división de entero entre entero da un resultado entero. Todos los
demás casos producen flotantes.
Hagamos algunas pruebas mas avanzadas.

Observa el siguiente fragmento de código:

print(6 // 4) salida 1

print(6. // 4) salida 1.0

Imagina que se utilizó  /  en lugar de  //  - ¿Podrías predecir los resultados?

Si, sería  1.5  en ambos casos. Eso esta claro.

Pero, ¿Qué resultado se debería esperar con una división  // ?

Ejecuta el código y observa por ti mismo.

Lo que se obtiene son dos unos, uno entero y uno flotante.

El resultado de la división entera siempre se redondea al valor entero inferior mas cercano del
resultado de la división no redondeada.

Esto es muy importante: el redondeo siempre va hacia abajo.

Observa el código e intenta predecir el resultado nuevamente:

print(-6 // 4) salida -2

print(6. // -4) salida -2.0

Nota: Algunos de los valores son negativos. Esto obviamente afectara el resultado. ¿Pero cómo?

El resultado es un par de dos negativos. El resultado real (no redondeado) es  -1.5  en ambo casos.
Sin embargo, los resultados se redondean. El redondeo se hace hacia el valor inferior entero,
dicho valor es  -2 , por lo tanto los resultados son:  -2  y  -2.0 .

NOTA

La division entera también se le suele llamar en inglés floor division. Más adelante te cruzarás con
este término.

Operadores: residuo (módulo)


El siguiente operador es uno muy peculiar, porque no tiene un equivalente dentro de
los operadores aritméticos tradicionales.

Su representación gráfica en Python es el símbolo de  %  (porcentaje), lo cual puede ser


un poco confuso.

Piensa en el como una diagonal (operador de división) acompañado por dos pequeños
círculos.

El resultado de la operación es el residuo que queda de la división entera.

En otras palabras, es el valor que sobra después de dividir un valor entre otro para
producir un resultado entero.

Nota: el operador en ocasiones también es denominado módulo en otros lenguajes


de programación.

Observa el fragmento de código – intenta predecir el resultado y después ejecútalo:

print(14 % 4)

Como puedes observar, el resultado es dos. Esta es la razón:

 14 // 4  da como resultado un  3  → esta es la parte entera, es decir


el cociente.
 3 * 4  da como resultado  12  → como resultado de la multiplicación entre el
cociente y el divisor.
 14 - 12  da como resultado  2  → este es el residuo.

El siguiente ejemplo es un poco mas complicado:

print(12 % 4.5)

¿Cuál es el resultado?

Revisar

3.0  - no  3  pero  3.0  (la regla aun funciona:  12 // 4.5  da  2.0 ;  2.0 * 4.5  da  9.0 ;  12
- 9.0  da  3.0 )
Operadores: como no dividir
Como probablemente sabes, la división entre cero no funciona.

No intentes:

 Dividir entre cero.


 Realizar una división entera entre cero.
 Encontrar el residuo de una división entre cero.

Operadores: suma
El símbolo del operador de suma es el  +  (signo de más), el cual esta completamente alineado a los
estándares matemáticos.

De nuevo, observa el siguiente fragmento de código:

print(-4 + 4)
print(-4. + 8)

El resultado no debe de sorprenderte. Ejecuta el código y revisa los resultados.

El operador de resta, operadores unarios y binarios


El símbolo del operador de resta es obviamente  -  (el signo de menos), sin embargo debes notar que
este operador tiene otra función - puede cambiar el signo de un número.

Esta es una gran oportunidad para mencionar una distinción muy importante entre
operadores unarios y binarios.

En aplicaciones de resta, el operador de resta espera dos argumentos: el izquierdo


(un minuendo en términos aritméticos) y el derecho (un sustraendo).

Por esta razón, el operador de resta es considerado uno de los operadores binarios, así como los
demás operadores de suma, multiplicación y división.

Pero el operador negativo puede ser utilizado de una forma diferente, observa la ultima línea de
código del siguiente fragmento:

print(-4 - 4) salida -8
print(4. - 8) salida -4.0
print(-1.1) salida -1.1
Por cierto: también hay un operador  +  unario. Se puede utilizar de la siguiente manera:

print(+2)

El operador conserva el signo de su único argumento, el de la derecha.

Aunque dicha construcción es sintácticamente correcta, utilizarla no tiene mucho sentido, y sería
difícil encontrar una buena razón para hacerlo.

Observa el fragmento de código que está arriba - ¿Puedes adivinar el resultado o salida?

Operadores y sus prioridades


Hasta ahora, se ha tratado cada operador como si no tuviera relación con los otros. Obviamente,
dicha situación tan simple e ideal es muy rara en la programación real.

También, muy seguido encontrarás más de un operador en una expresión, y entonces esta presunción
ya no es tan obvia.

Considera la siguiente expresión:

2 + 3 * 5

Probablemente recordaras de la escuela que las multiplicaciones preceden a las sumas.

Seguramente recordaras que primero se debe multiplicar 3 por 5, mantener el 15 en tu memoria y


después sumar el 2, dando como resultado el 17.

El fenómeno que causa que algunos operadores actúen antes que otros es conocido como la
jerarquía de prioridades.

Python define la jerarquía de todos los operadores, y asume que los operadores de mayor jerarquía
deben realizar sus operaciones antes que los de menor jerarquía.

Entonces, si se sabe que la  *  tiene una mayor prioridad que la  + , el resultado final debe de ser
obvio.

Operadores y sus enlaces


El enlace de un operador determina el orden en que se computan las operaciones de los operadores
con la misma prioridad, los cuales se encuentran dentro de una misma expresión.

La mayoría de los operadores de Python tienen un enlazado hacia la izquierda, lo que significa que el
calculo de la expresión es realizado de izquierda a derecha.
Este simple ejemplo te mostrará como funciona. Observa:

print(9 % 6 % 2)

Existen dos posibles maneras de evaluar la expresión:

 De izquierda a derecha: primero  9 % 6  da como resultado  3 , y entonces  3 % 2  da como


resultado  1 .
 De derecha a izquierda: primero  6 % 2  da como resultado  0 , y entonces  9 % 0  causa un
error fatal.

Ejecuta el ejemplo y observa lo que se obtiene.

El resultado debe ser  1 . El operador tiene un enlazado hacia la izquierda. Pero hay una excepción
interesante.

Operadores y sus enlaces: exponenciación


Repite el experimento, pero ahora con exponentes.

Utiliza este fragmento de código:

print(2 ** 2 ** 3) salida 256

Los dos posibles resultados son:

 2 ** 2  →  4 ;  4 ** 3  →  64
 2 ** 3  →  8 ;  2 ** 8  →  256

Ejecuta el código, ¿Qué es lo que observas?

El resultado muestra claramente que el operador de exponenciación utiliza


enlazado hacia la derecha.

Lista de prioridades
Como eres nuevo a los operadores de Python, no se presenta por ahora una lista completa de las
prioridades de los operadores.

En lugar de ello, se mostrarán solo algunos, y se irán expandiendo conforme se vayan introduciendo
operadores nuevos.

Observa la siguiente tabla:

Prioridad Operador
1 + ,  - unario
2 **
3 * ,  / ,  %
4 + ,  - binario

Nota: se han enumerado los operadores en orden de la mas alta (1) a la mas baja (4) prioridad.

Intenta solucionar la siguiente expresión:

print(2 * 3 % 5)

Ambos operadores ( *  y  % ) tienen la misma prioridad, el resultado solo se puede obtener conociendo
el sentido del enlazado. ¿Cuál será el resultado?

Revisar

Operadores y paréntesis
Por supuesto, se permite hacer uso de paréntesis, lo cual cambiará el orden natural del cálculo de la
operación.

De acuerdo con las reglas aritméticas, las sub-expresiones dentro de los paréntesis siempre se
calculan primero.

Se pueden emplear tantos paréntesis como se necesiten, y seguido son utilizados para mejorar la
legibilidad de una expresión, aun si no cambian el orden de las operaciones.

Un ejemplo de una expresión con múltiples paréntesis es la siguiente:

print((5 * ((25 % 13) + 100) / (2 * 13)) // 2)


Intenta calcular el valor que se calculará en la consola. ¿Cuál es el resultado de la
función  print() ?

Revisar

10.0

Lista de prioridades
Como eres nuevo a los operadores de Python, no se presenta por ahora una lista completa de las
prioridades de los operadores.

En lugar de ello, se mostrarán solo algunos, y se irán expandiendo conforme se vayan introduciendo
operadores nuevos.

Observa la siguiente tabla:

Prioridad Operador
1 + ,  - unario
2 **
3 * ,  / ,  %
4 + ,  - binario

Nota: se han enumerado los operadores en orden de la mas alta (1) a la mas baja (4) prioridad.

Intenta solucionar la siguiente expresión:

print(2 * 3 % 5)

Ambos operadores ( *  y  % ) tienen la misma prioridad, el resultado solo se puede obtener conociendo
el sentido del enlazado. ¿Cuál será el resultado?

Revisar

Operadores y paréntesis
Por supuesto, se permite hacer uso de paréntesis, lo cual cambiará el orden natural del cálculo de la
operación.

De acuerdo con las reglas aritméticas, las sub-expresiones dentro de los paréntesis siempre se
calculan primero.

Se pueden emplear tantos paréntesis como se necesiten, y seguido son utilizados para mejorar la
legibilidad de una expresión, aun si no cambian el orden de las operaciones.

Un ejemplo de una expresión con múltiples paréntesis es la siguiente:

print((5 * ((25 % 13) + 100) / (2 * 13)) // 2)

Intenta calcular el valor que se calculará en la consola. ¿Cuál es el resultado de la


función  print() ?

Revisar

10.0

RESUMEN DE LA SECCIÓN

Puntos Clave
1. Una expresión es una combinación de valores (o variables, operadores, llamadas a funciones,
aprenderás de ello pronto) las cuales son evaluadas y dan como resultado un valor, por
ejemplo,  1+2 .

2. Los operadores son símbolos especiales o palabras clave que son capaces de operar en los valores
y realizar operaciones matemáticas, por ejemplo, el  *  multiplica dos valores:  x*y .

3. Los operadores aritméticos en Python:  +  (suma),  -  (resta),  *  (multiplicación),  /  (división


clásica: regresan un flotante si uno de los valores es de este tipo),  %  (módulo: divide el operando
izquierdo entre el operando derecho y regresa el residuo de la operación, por
ejemplo,  5%2=1 ),  **  (exponenciación: el operando izquierdo se eleva a la potencia del operando
derecho, por ejemplo,  2**3=2*2*2=8 ),  //  (división entera: retorna el numero resultado de la
división, pero redondeado al numero entero inferior más cercano, por ejemplo,  3//2.0=1.0 ).

4. Un operador unario es un operador con solo un operando, por ejemplo,  -1 , o  +3 .

5. Un operador binario es un operador con dos operados, por ejemplo,  4+5 , o  12%5 .

6. Algunos operadores actúan antes que otros, a esto se le llama - jerarquía de prioridades:

 Unario  +  y  -  tienen la prioridad más alta.


 Después:  ** , después:  * ,  / , y  % , y después la prioridad más baja: binaria  +  y  - .
7. Las sub-expresiones dentro de paréntesis siempre se calculan primero, por ejemplo,  15-
1*(5*(1+2))=0 .

8. Los operadores de exponenciación utilizan enlazado hacia la derecha, por


ejemplo,  2**2**3=256 .

Ejercicio 1

¿Cuál es la salida del siguiente fragmento de código?

print((2**4), (2*4.), (2*4))


Revisar

16 8.0 8

Ejercicio 2

¿Cuál es la salida del siguiente fragmento de código?

print((-2/4), (2/4), (2//4), (-2//4))


Revisar

-0.5 0.5 0 -1

Ejercicio 3

¿Cuál es la salida del siguiente fragmento de código?

print((2%-4), (2%4), (2**3**2))


Revisar

-2 2 512

 2.1.4.1 Variables - cajas en forma de datos

¿Qué son las Variables?


Es justo que Python nos permita codificar literales, las cuales contengan valores numéricos y
cadenas.

Ya hemos visto que se pueden hacer operaciones aritméticas con estos números: sumar, restar, etc.
Esto se hará una infinidad de veces en un programa.

Pero es normal preguntar como es que se pueden almacenar los resultados de estas operaciones,
para poder emplearlos en otras operaciones, y así sucesivamente.

¿Cómo almacenar los resultados intermedios, y después utilizarlos de nuevo para producir resultados
subsecuentes?

Python ayudará con ello. Python ofrece "cajas" (contenedores) especiales para este propósito, estas
cajas son llamadas variables - el nombre mismo sugiere que el contenido de estos contenedores
puede variar en casi cualquier forma.

¿Cuáles son los componentes o elementos de una variable en Python?

 Un nombre.
 Un valor (el contenido del contenedor).

Comencemos con lo relacionado al nombre de la variable.

Las variables no aparecen en un programa automáticamente. Como desarrollador, tu debes decidir


cuantas variables deseas utilizar en tu programa.

También las debes de nombrar.

Si se desea nombrar una variable, se deben seguir las siguientes reglas:

 El nombre de la variable debe de estar compuesto por MAYUSCULAS,


minúsculas, dígitos, y el carácter  _  (guion bajo).
 El nombre de la variable debe comenzar con una letra.
 El carácter guion bajo es considerado una letra.
 Las mayúsculas y minúsculas se tratan de forma distinta (un poco diferente
que en el mundo real - Alicia y ALICIA son el mismo nombre, pero en Python son
dos nombres de variable distintos, subsecuentemente, son dos variables
diferentes).
 El nombre de las variables no pueden ser igual a alguna de las palabras
reservadas de Python (se explicará más de esto pronto).

Nombres correctos e incorrectos de variables


Nota que la misma restricción aplica a los nombres de funciones.

Python no impone restricciones en la longitud de los nombres de las variables, pero eso no significa
que un nombre de variable largo sea mejor que uno corto.
Aquí se muestran algunos nombres de variable que son correctos, pero que no siempre son
convenientes:

MiVariable ,  i ,  t34 ,  Tasa_Cambio ,  contador ,  DiasParaNavidad ,  ElNombreEsTanLa


rgoQueSeCometeranErroresConEl ,  _ .

Además, Python permite utilizar no solo las letras latinas, sino caracteres específicos de otros
idiomas que utilizan otros alfabetos.

Estos nombres de variables también son correctos:

Adiós_Señora ,  sûr_la_mer ,  Einbahnstraße ,  переменная .

Ahora veamos algunos nombres incorrectos:

10t  (no comienza con una letra),  Tasa Cambio  (contiene un espacio).

Palabras Clave
Observa las palabras que juegan un papel muy importante en cada programa de Python.

['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class',


'continue', 'def', 'del', 'elif', 'else', 'except', 'finally',
'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda',
'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while',
'with', 'yield']

Son llamadas palabras clave o (mejor dicho) palabras reservadas. Son reservadas porque no se


deben utilizar como nombres: ni para variables, ni para funciones, ni para cualquier otra cosa que
se desee crear.

El significado de la palabra reservada está predefinido, y no debe cambiar.

Afortunadamente, debido al hecho de que Python es sensible a mayúsculas y minúsculas, cualquiera


de estas palabras se pueden modificar cambiando una o varias letras de mayúsculas a minúsculas o
viceversa, creando una nueva palabra, la cual no esta reservada.

Por ejemplo - no se puede nombrar a la variable así:

import

No se puede tener una variable con ese nombre, esta prohibido, pero se puede hacer lo siguiente:
Import

Estas palabras podrían parecer un misterio ahorita, pero pronto se aprenderá acerca de su significado.

Creando variables
¿Qué se puede poner dentro de una variable?

Cualquier cosa.

Se puede utilizar una variable para almacenar cualquier tipo de los valores que ya se
han mencionado, y muchos mas de los cuales aun no se han explicado.

El valor de la variable en lo que se ha puesto dentro de ella. Puede variar tanto como
se necesite o requiera. El valor puede ser entero, después flotante, y eventualmente
ser una cadena.

Hablemos de dos cosas importantes - como son creadas las variables, y como poner
valores dentro de ellas (o mejor dicho, como dar o pasarles valores).

RECUERDA

Una variable se crea cuando se le asigna un valor. A diferencia de otros lenguajes


de programación, no es necesario declararla.

Si se le asigna cualquier valor a una variable no existente, la variable


será automáticamente creada. No se necesita hacer algo más.

La creación (o su sintaxis) es muy simple: solo utiliza el nombre de la variable


deseada, después el signo de igual (=) y el valor que se desea colocar dentro de la
variable.

Observa el siguiente fragmento de código:

var = 1
print(var)

Consiste de dos simples instrucciones:

 La primera crea una variable llamada  var , y le asigna un literal con un valor
entero de  1 .
 La segunda imprime el valor de la variable recientemente creada en la consola.

Nota:  print()  tiene una función más – puede manejar variables también. ¿Puedes
predecir cual será la salida (resultado) del código?
Revisar

Utilizando variables
Se tiene permitido utilizar cuantas declaraciones de variables sean necesarias para lograr el objetivo
del programa, por ejemplo:

var = 1
balance_cuenta = 1000.0
nombreCliente = 'John Doe'
print(var, balance_cuenta, nombreCliente)
print(var)

Sin embargo, no se permite utilizar una variable que no exista, (en otras palabras, una variable a la
cual no se le a dado un valor).

Este ejemplo ocasionara un error:

var = 1
print(Var)

Se ha tratado de utilizar la variable llamada  Var , la cual no tiene ningún valor


(nota:  var  y  Var  son entidades diferentes, y no tienen nada en común dentro de Python).

RECUERDA

Se puede utilizar  print()  para combinar texto con variables utilizando el operador  +  para mostrar
cadenas con variables, por ejemplo:

var = "3.7.1"
print("Versión de Python: " + var)

¿Puedes predecir la salida del fragmento de código?

Revisar

Versión de Python: 3.7.1

Asignar un valor nuevo a una variable ya existente


¿Cómo se le asigna un valor nuevo a una variable que ya ha sido creada? De la misma manera. Solo
se necesita el signo de igual.

El signo de igual es de hecho un operador de asignación. Aunque esto suene un poco extraño, el
operador tiene una sintaxis simple y una interpretación clara y precisa.

Asigna el valor del argumento de la derecha al de la izquierda, aún cuando el argumento de la


derecha sea una expresión arbitraria compleja que involucre literales, operadores y variables
definidas anteriormente.

Observa el siguiente código:

var = 1
print(var)
var = var + 1
print(var)

El código envía dos líneas a la consola:

1
2

La primer línea del código crea una nueva variable llamada  var  y le asigna el valor de  1 .

La declaración se lee de la siguiente manera: asigna el valor de  1  a una variable llamada  var .

De manera mas corta: asigna  1  a  var .

Algunos prefieren leer el código así:  var  se convierte en  1 .

La tercera línea le asigna a la misma variable un nuevo valor tomado de la variable misma,


sumándole  1 . Al ver algo así, un matemático probablemente protestaría, ningún valor puede ser
igualado a si mismo mas uno. Esto es una contradicción. Pero Python trata el signo  =  no como igual
a, sino como asigna un valor.

Entonces, ¿Cómo se lee esto en un programa?

Toma el valor actual de la variable  var , sumale  1  y guárdalo en la variable  var .

En efecto, el valor de la variable  var  ha sido incrementado por uno, lo cual no está relacionado
con comparar la variable con otro valor.
¿Puedes predecir cuál será el resultado del siguiente fragmento de código?

var = 100
var = 200 + 300
print(var)

Revisar

500  - ¿Porque? Bueno, primero, la variable  var  es creada y se le asigna el valor de 100. Después, a
la misma variable se le asigna un nuevo valor: el resultado de sumarle 200 a 300, lo cual es 500.

Resolviendo problemas matemáticos simples


Ahora deberías de ser capaz de construir un corto programa el cual resuelva
problemas matemáticos sencillos como el Teorema de Pitágoras:

El cuadrado de la hipotenusa es igual a la suma de los cuadrados de los dos catetos.

El siguiente código evalúa la longitud de la hipotenusa (es decir, el lado más largo de
un triangulo rectángulo, el opuesto al ángulo recto) utilizando el Teorema de Pitágoras:

a = 3.0
b = 4.0
c = (a ** 2 + b ** 2) ** 0.5
print("c =", c)

Nota: se necesita hacer uso del operador  **  para evaluar la raíz cuadrada:

√ (x)  = x(½)
y

c = √ a2 + b2 
¿Puedes predecir la salida del código?

Revisa abajo y ejecuta el código en el editor para confirmar tus predicciones.

Revisar

c = 5.0

LABORATORIO
Tiempo Estimado
10 minutos

Nivel de dificultad
Fácil

Objetivos
 Familiarizarse con el concepto de almacenar y trabajar con diferentes tipos de
datos en Python.
 Experimentar con el código en Python.

Escenario
A continuación una historia:

Érase una vez en la Tierra de las Manzanas, Juan tenía tres manzanas, María tenía
cinco manzanas, y Adán tenía seis manzanas. Todos eran muy felices y vivieron por
muchísimo tiempo. Fin de la Historia.

Tu tarea es:

 Crear las variables:  juan ,  maria , y  adan .


 Asignar valores a las variables. El valor debe de ser igual al numero de
manzanas que cada quien tenía.
 Una vez almacenados los números en las variables, imprimir las variables en
una línea, y separar cada una de ellas con una coma.
 Después se debe crear una nueva variable llamada  totalManzanas  y se debe
igualar a la suma de las tres variables anteriores.
 Imprime el valor almacenado en  totalManzanas  en la consola.
 Experimenta con tu código: crea nuevas variables, asigna diferentes valores a
ellas, y realiza varias operaciones aritméticas con ellas (por ejemplo, +, -, *, /, //,
etc.). Intenta poner una cadena con un entero juntos en la misma línea, por
ejemplo,  "Numero Total de Manzanas:"  y  totalManzanas .

juan=3
maria=5
adan=6
print(juan,maria,adan,sep=”,”)
totalmanzanas=(juan+maria+adan)
print(totalmanzanas)
print(“Número total de Manzanas:”,totalmanzanas)
Operadores Abreviados
Es tiempo de explicar el siguiente conjunto de operadores que harán la vida del
programador/desarrollador mas fácil.

Muy seguido, se desea utilizar la misma variable al lado derecho y al lado izquierdo del operador  = .

Por ejemplo, si se necesita calcular una serie de valores sucesivos de la potencia de 2, se puede usar
el siguiente código:

x = x * 2

También, puedes utilizar una expresión como la siguiente si no puedes dormir y estas tratando de
resolverlo con alguno de los métodos tradicionales:

oveja = oveja + 1

Python ofrece una manera mas corta de escribir operaciones como estas, lo cual se puede codificar
de la siguiente manera:

x *= 2
oveja+= 1

A continuación se intenta presentar una descripción general para este tipo de operaciones.

Si  op  es un operador de dos argumentos (esta es una condición muy imporante) y el operador es
utilizado en el siguiente contexto:

variable = variable op expresión

Puede ser simplificado de la siguiente manera:

variable op= expresión

Observa los siguientes ejemplos. Asegúrate de entenderlos todos.

i = i + 2 * j  ⇒  i += 2 * j

var = var / 2  ⇒  var /= 2

rem = rem % 10  ⇒  rem %= 10

j = j - (i + var + rem)  ⇒  j -= (i + var + rem)

x = x ** 2  ⇒  x **= 2
LABORATORIO

Tiempo estimado
10 minutos

Nivel de dificultad
Fácil

Objetivos
 Familiarizarse con el concepto de variables y trabajar con ellas.
 Realizar operaciones básicas y conversiones.
 Experimentar con el código de Python.

Escenario
Millas y kilómetros son unidades de longitud o distancia.

Teniendo en mente que  1  equivale aproximadamente a  1.61  kilómetros,


complemente el programa en el editor para que convierta de:

 Millas a kilómetros.
 Kilómetros a millas.

No se debe cambiar el código existente. Escribe tu código en los lugares indicados


con  ### . Prueba tu programa con los datos que han sido provistos en el código fuente.

Pon mucha atención a lo que esta ocurriendo dentro de la función  print() . Analiza
como es que se proveen múltiples argumentos para la función, y como es que se
muestra el resultado.

Nota que algunos de los argumentos dentro de la función  print()  son cadenas (por
ejemplo  "millas son" , y otros son variables (por ejemplo  millas ).

CONSEJO

Hay una cosa interesante mas que esta ocurriendo. ¿Puedes ver otra función dentro
de la función  print()  ? Es la función  round() . Su trabajo es redondear la salida del
resultado al numero de decimales especificados en el paréntesis, y regresar un valor
flotante (dentro de la función  round()  se puede encontrar el nombre de la variable, el
nombre, una coma, y el numero de decimales que se desean mostrar). Se hablará mas
de esta función muy pronto, no te preocupes si no todo queda muy claro. Solo se
quiere impulsar tu curiosidad.

Después de completar el laboratorio , abre Sandbox (el arenero), y experimenta más.


Intenta escribir diferentes convertidores, por ejemplo, un convertidor de USD a EUR,
un convertidor de temperatura, etc. – ¡deja que tu imaginación vuele! Intenta
mostrar los resultados combinando cadenas y variables. Intenta utilizar y
experimentar con la función  round()  para redondear tus resultados a uno, dos o tres
decimales. Revisa que es lo que sucede si no se provee un dígito al redondear.
Recuerda probar tus programas.

Experimenta, saca tus propias conclusiones, y aprende. Se curioso.

Resultado Esperado
7.38 millas son 11.88 kilómetros

12.25 kilómetros son 7.61 millas


kilometros = 12.25
millas = 7.38

millas_a_kilometros = millas*1.61
kilometros_a_millas = kilometros/1.61

print(millas, " millas son ", round(millas_a_kilometros, 2), " kilómetros ")
print(kilometros, " kilómetros son ", round(kilometros_a_millas, 2), " millas ")

LABORATORIO

Tiempo Estimado
10-15 minutos

Nivel de Dificultad
Fácil

Objetivos
 Familiarizarse con los conceptos de números, operadores y operaciones
aritméticas en Python.
 Realizar cálculos básicos.
Escenario
Observa el código en el editor: lee un valor  flotante , lo coloca en una variable
llamada  x , e imprime el valor de la variable llamada  y . Tu tarea es completar el código
para evaluar la siguiente expresión:

3x3 - 2x2 + 3x - 1

El resultado debe ser asignado a  y .

Recuerda que la notación algebraica clásica muy seguido omite el operador de


multiplicación, aquí se debe de incluir de manera explicita. Nota como se cambia el
tipo de dato para asegurarnos de que  x  es del tipo  flotante .

Mantén tu código limpio y legible, y pruébalo utilizando los datos que han sido
proporcionados. No te desanimes por no lograrlo en el primer intento. Se persistente
y curioso.

Prueba de Datos
Datos de Muestra

x = 0

x = 1

x = -1

Salida Esperada

y = -1.0

y = 3.0

y = -9.0

# codifica aquí tus datos de prueba.

x = valor x

x = float(x)

# escribe tu código aquí.

y = 3*(x**3)-2*(x**2)+3*(x)-1
print("y =", y)

RESUMEN DE LA SECCIÓN

Puntos Clave

1. Una variable es una ubicación nombrada reservada para almacenar valores en la memoria. Una
variable es creada o inicializada automáticamente cuando se le asigna un valor por primera vez.

2. Cada variable debe de tener un nombre único - un identificador. Un nombre valido debe ser aquel
que no contiene espacios, debe comenzar con un guion bajo ( _ ), o una letra, y no puede ser una
palabra reservada de Python. El primer carácter puede estar seguido de guiones bajos, letras, y
dígitos. Las variables en Python son sensibles a mayúsculas y minúsculas.

3. Python es un lenguaje de tipo dinámico, lo que significa que no se necesita declarar variables en


él. Para asignar valores a las variables, se utiliza simplemente el operador de asignación, es decir el
signo de igual ( = ) por ejemplo,  var = 1 .

4. También es posible utilizar operadores de asignación compuesta (operadores abreviados) para


modificar los valores asignados a las variables, por ejemplo,  var += 1 , or  var /= 5 * 2 .

5. Se les puede asignar valores nuevos a variables ya existentes utilizando el operador de asignación
o un operador abreviado:

var = 2
print(var)

var = 3
print(var)

var += 1
print(var)

6. Se puede combinar texto con variables empleado el operador  + , y utilizar la


función  print()  para mostrar o imprimir los resultados, por ejemplo:

var = "007"
print("Agente " + var)

Ejercicio 1
¿Cuál es el resultado del siguiente fragmento de código?

var = 2
var = 3
print(var)
Revisar

Ejercicio 2

¿Cuáles de los siguientes nombres de variables son ilegales en Python?

my_var
m
101
averylongvariablename
m101
m 101
Del
del
Revisar

my_var

101 # incorrecto (comienza con un digito)

averylongvariablename

m101

m 101 # incorrecto (contiene un espacio)

Del

del # incorrecto (es una palabra clave)

Ejercicio 3

¿Cuál es el resultado del siguiente fragmento de código?

a = '1'
b = "1"
print(a + b)
Revisar

11

Ejercicio 4

¿Cuál es el resultado del siguiente fragmento de código?

a = 6
b = 3
a /= 2 * b
print(a)
Revisar

1.0
2*b=6
a = 6 → 6 / 6 = 1.0

 2.1.5.1 Comentarios

Poner comentarios en el código: ¿por qué, cuándo y


dónde?
Quizá en algún momento será necesario poner algunas palabras en el código dirigidas no a Python,
sino a las personas quienes estén leyendo el código con el fin de explicarles como es que funciona, o
tal vez especificar el significado de las variables, también para documentar quien es el autor del
programa y en que fecha fue escrito.

Un texto insertado en el programa el cual es, omitido en la ejecución, es denominado


un comentario.

¿Cómo se colocan este tipo de comentarios en el código fuente? Tiene que ser hecho de cierta
manera para que Python no intente interpretarlo como parte del código.

Cuando Python se encuentra con un comentario en el programa, el comentario es completamente


transparente, desde el punto de vista de Python, el comentario es solo un espacio vacío, sin importar
que tan largo sea.

En Python, un comentario es un texto que comienza con el símbolo  #  y se extiende hasta el final de
la línea.

Si se desea colocar un comentario que abarca varias líneas, se debe colocar este símbolo en cada
línea.

Justo como el siguiente código:


# Esta programa calcula la hipotenusa (c)

# a y b son las longitudes de los catetos

a = 3.0

b = 4.0

c = (a ** 2 + b ** 2) ** 0.5 # se utiliza ** en lugar de la raíz


cuadrada

print("c =", c)

Los desarrolladores buenos y responsables describen cada pieza importante de código, por


ejemplo, el explicar el rol de una variable; aunque la mejor manera de comentar una variable es
dándole un nombre que no sea ambiguo.

Por ejemplo, si una variable determinada esta diseñada para almacenar el área de un cuadrado, el
nombre  areaCuadrado  será muchísimo mejor que  tiaJuana .

El primer nombre dado a la variable se puede definir como auto-comentable.

Los comentarios pueden ser útiles en otro aspecto, se pueden utilizar para marcar un fragmento de
código que actualmente no se necesita, cual sea la razón. Observa el siguiente ejemplo, sí
se descomenta la línea resaltada, esto afectara la salida o resultado del código:

# Este es un programa de prueba

x = 1

y = 2

# y = y + x

print(x + y)

Esto es frecuentemente realizado cuando se esta probando un programa, con el fin de aislar un
fragmento de código donde posiblemente se encuentra un error.

LABORATORIO

Tiempo Estimado
5 minutos
Nivel de Dificultad
Muy Fácil

Objetivos
 Familiarizarse con el concepto de comentarios en Python.
 Utilizar y no utilizar los comentarios.
 Reemplazar los comentarios con código.
 Experimentar con el código de Python.

Escenario
El código en el editor contiene comentarios. Intenta mejorarlo: agrega o quita
comentarios donde consideres que sea apropiado (en ocasiones el remover un
comentario lo hace mas legible), además, cambia el nombre de las variables donde
consideres que esto mejorará la comprensión del código.

NOTA

Los comentarios son muy importantes. No solo hacen que el programa sea más fácil
de entender, pero también sirven para deshabilitar aquellas partes de código que
no son necesarias (por ejemplo, cuando se necesita probar cierta parte del código, e
ignorar el resto). Los buenos programadores describen cada parte importante del
código, y dan nombres significativos a variables, debido a que en ocasiones es
mucho más sencillo dejar el comentario dentro del código mismo.

Es bueno utilizar nombres de variables legibles, y en ocasiones es mejor dividir el


código en partes con nombres (por ejemplo en funciones). En algunas situaciones, es
una buena idea escribir los pasos de como se realizaron los cálculos de una forma
sencilla y clara.

Una cosa mas: puede ocurrir que un comentario contenga una pieza de información
incorrecta o errónea, nunca se debe de hacer eso a propósito.

#este programa calcula los segundos en cierto número de horas determinadas

# este programa fue escrito hace dos días

a = 5# numero de horas

segundos = 3600 # número de segundos en una hora

print("Horas: ", a) #imprime el numero de horas

print("Segundos en horas: ", a * segundos) # se imprime el numero de segundos en


determinado numero de horas
print("Adiós")#aquí también se debe de imprimir un "Adiós", pero el programador no tuvo
tiempo de escribirlo

print("Numero de segundos en 2 horas:", 2*segundos, "segundos")#este el es fin del programa


que calcula el numero de segundos en 2 horas

RESUMEN DE SECCIÓN

Puntos Clave

1. Los comentarios pueden ser utilizados para colocar información adicional en el código. Son
omitidos al momento de la ejecución. Dicha información es para los lectores que están manipulando
el código. En Python, un comentario es un fragmento de texto que comienza con un  # . El
comentario se extiende hasta el final de la línea.

2. Si deseas colocar un comentario que abarque varias líneas, es necesario colocar un  #  al inicio de
cada línea. Además, se puede utilizar un comentario para marcar un fragmento de código que no es
necesaria en el momento y no se desea ejecutar. (observa la ultima línea de código del siguiente
fragmento), por ejemplo:

# Este programa imprime


# un saludo en pantalla
print("Hola!") # Se invoca la función print() function
# print("Soy Python.")

3. Cuando sea posible, se deben auto comentar los nombres de las variables, por ejemplo, si se
están utilizando dos variables para almacenar la altura y longitud de algo, los
nombres  altura  y  longitud  son una mejor elección que  mivar1  y  mivar2 .

4. Es importante utilizar los comentarios para que los programas sean más fáciles de entender,
además de emplear variables legibles y significativas en el código. Sin embargo, es igualmente
importante no utilizar nombres de variables que sean confusos, o dejar comentarios que contengan
información incorrecta.

5. Los comentarios pueden ser muy útiles cuando tu estas leyendo tu propio código después de un
tiempo (es común que los desarrolladores olviden lo que su propio código hace), y
cuando otros están leyendo tu código (les puede ayudar a comprender que es lo que hacen tus
programas y como es que lo hacen).

Ejercicio 1
¿Cuál es la salida del siguiente fragmento de código?

# print("Cadena #1")
print("Cadena #2")
Revisar

Cadena #2

Ejercicio 2

¿Qué ocurrirá cuando se ejecute el siguiente código?

# Esto es
un comentario
en varias líneas #

print("Hola!")
Revisar

SyntaxError: invalid syntax

 2.1.6.1 Cómo hablar con una computadora

La función input()
Ahora se introducirá una nueva función, la cual pareciese ser un reflejo de la función  print() .

¿Por que? Bueno,  print()  envía datos a la consola.

Esta nueva función obtiene datos de ella.

print()  no tiene un resultado utilizable. La importancia de esta nueva función es que regresa un
valor muy utilizable.

La función se llama  input() . El nombre de la función lo dice todo.

La función  input()  es capaz de leer datos que fueron introducidos por el usuario y pasar esos
datos al programa en ejecución.

El programa entonces puede manipular los datos, haciendo que el código sea verdaderamente
interactivo.

Todos los programas leen y procesan datos. Un programa que no obtiene datos de entrada del
usuario es un programa sordo.

Observa el ejemplo:
print("Dime algo...")

algo = input()

print("Mmm...", algo, "...¿en serio?")

Se muestra un ejemplo muy sencillo de como utilizar la función  input() .

Nota:

 El programa solicita al usuario que inserte algún dato desde la consola (seguramente


utilizando el teclado, aunque también es posible introducir datos utilizando la voz o alguna
imagen).
 La función  input()  es invocada sin argumentos (es la manera mas sencilla de utilizar la
función); la función pondrá la consola en modo de entrada; aparecerá un cursor que
parpadea, y podrás introducir datos con el teclado, al terminar presiona la tecla Enter; todos
los datos introducidos serán enviados al programa a través del resultado de la función.
 Nota: el resultado debe ser asignado a una variable; esto es crucial, si no se hace los datos
introducidos se perderán.
 Después se utiliza la función  print()  para mostrar los datos que se obtuvieron, con
algunas observaciones adicionales.

Intenta ejecutar el código y permite que la función te muestre lo que puede hacer.

La función input() con un argumento
La función  input()  puede hacer algo más: puede mostrar un mensaje al usuario sin la ayuda de la
función  print() .

Se ha modificado el ejemplo un poco, observa el código:

algo = input("Dime algo...")

print("Mmm...", algo, "...¿En serio?")

Nota:

 La función  input()  al ser invocada con un argumento, contiene una cadena con un
mensaje.
 El mensaje será mostrado en consola antes de que el usuario tenga oportunidad de escribir
algo.
 Después de esto  input()  hará su trabajo.

Esta variante de la invocación de la función  input()  simplifica el código y lo hace más claro.

El resultado de la función input()
Se ha dicho antes, pero hay que decirlo sin ambigüedades una vez más: el resultado de la
función  input()  es una cadena.

Una cadena que contiene todos los caracteres que el usuario introduce desde el teclado. No es un
entero ni un flotante.

Esto significa que no se debe utilizar como un argumento para operaciones matemáticas, por
ejemplo, no se pueden utilizar estos datos para elevarlos al cuadrado, para dividirlos entre algo o por
algo.

cualquierNumero = input("Inserta un número: ")

algo = cualquierNumero ** 2.0

print(cualquierNumero, "al cuadrado es", algo)

La función input() - operaciones prohibidas


Observa el código en el editor. Ejecútalo, inserta cualquier número, y oprime Enter.

¿Qué es lo que ocurre?

Python debió haberte dado la siguiente salida:

Traceback (most recent call last):


File ".main.py", line 4, in <module>
resultado = algo ** 2.0
TypeError: unsupported operand type(s) for ** or pow(): 'str' and
'float'

La última línea lo explica todo, se intentó aplicar el operador  **  a  'str'  (una cadena)
acompañado por un  'float'  (valor flotante).

Esto está prohibido.

Esto debe de ser obvio – ¿Puedes predecir el valor de  "ser o no ser"  elevado a
la  2  potencia?

No podemos. Python tampoco puede.

¿Habremos llegado a un punto muerto? ¿Existirá alguna solución? Claro que la hay.

# Probando mensajes de error


algo = input("Inserta un número: ")
resultado = algo ** 2.0
print(algo, "al cuadrado es ", resultado)

onversión de datos o casting


Python ofrece dos simples funciones para especificar un tipo de dato y resolver este
problema, aquí están:  int()  y  float() .

Sus nombres indican cual es su función:

 La función  int()  toma un argumento (por ejemplo, una


cadena:  int(string) ) e intenta convertirlo a un valor entero; si llegase a fallar,
el programa entero fallará también (existe una manera de solucionar esto, se
explicará mas adelante).
 La función  float()  toma un argumento (por ejemplo, una
cadena:  float(string) ) e intenta convertirlo a flotante (el resto es lo mismo).

Esto es muy simple y muy efectivo. Sin embargo, estas funciones se pueden invocar
directamente pasando el resultado de la función  input()  directamente. No hay
necesidad de emplear variables como almacenamiento intermedio.

Se ha implementado esta idea en el editor, observa el código.

¿Puedes imaginar como la cadena introducida por el usuario fluye desde la


función  input()  hacía la función  print() ?

Intenta ejecutar el código modificado. No olvides introducir un número valido.

Prueba con diferentes valores, pequeños, grandes, negativos y positivos. El cero


también es un buen valor a introducir.

algo = float(input("Inserta un número: "))


resultado = algo ** 2.0
print(algo, "al cuadrado es", resultado)

Más acerca de la función input() y tipos de


conversión
El tener un equipo compuesto por  input() - int() - float()  abre muchas nuevas
posibilidades.

Eventualmente serás capaz de escribir programas completos, los cuales acepten datos
en forma de números, los cuales serán procesados y se mostrarán los resultados.

Por supuesto, estos programas serán muy primitivos y no muy utilizables, debido a
que no pueden tomar decisiones, y consecuentemente no son capaces de reaccionar
acorde a cada situación.

Sin embargo, esto no es un problema; se explicará como solucionarlo pronto.

El siguiente ejemplo hace referencia al programa anterior que calcula la longitud de la


hipotenusa. Vamos a reescribirlo, para que pueda leer las longitudes de los catetos
desde la consola.

Revisa la ventana del editor, así es como se ve ahora.

Este programa le preguntó al usuario los dos catetos, calcula la hipotenusa e imprime
el resultado.

Ejecútalo de nuevo e intenta introducir valores negativos.

El programa desafortunadamente, no reacciona correctamente a este error.

Vamos a ignorar esto por ahora. Regresaremos a ello pronto.

Debido a que la función  print()  acepta una expresión como argumento, se


puede quitar la variable del código.

Como se muestra en el siguiente código:

cateto_a = float(input("Inserta la longitud del primer cateto: "))


cateto_b = float(input("Inserta la longitud del segundo cateto "))
print("La longitud de la hipotenusa es: ", (cateto_a**2 +
cateto_b**2) ** .5)

cateto_a = float(input("Inserta la longitud del primer cateto: "))

cateto_b = float(input("Inserta la longitud del segundo cateto "))

hipo = (cateto_a**2 + cateto_b**2) ** .5

print("La longitud de la hipotenusa es: ", hipo)


Operadores de cadenas - introducción
Es tiempo de regresar a estos dos operadores aritméticos:  +  y  * .

Ambos tienen una función secundaría. Son capaces de hacer algo más
que sumar y multiplicar.

Los hemos visto en acción cuando sus argumentos son (flotantes o enteros).

Ahora veremos que son capaces también de manejar o manipular cadenas, aunque,
en una manera muy específica.

Concatenación
El sigo de  +  (más), al ser aplicado a dos cadenas, se convierte en un operador de
concatenación:

string + string

Simplemente concatena (junta) dos cadenas en una. Además, puede ser utilizado más


de una vez en una misma expresión.

En contraste con el operador aritmético, el operador de concatenación no


es conmutativo, por ejemplo,  "ab" + "ba"  no es lo mismo que  "ba" + "ab" .

No olvides, si se desea que el signo  +  sea un concatenador, no un sumador, solo se


debe asegurar que ambos argumentos sean cadenas.

No se pueden mezclar los tipos de datos aquí.

Este es un programa sencillo que muestra como funciona el signo  +  como


concatenador:

nom = input("¿Me puedes dar tu nombre por favor? ")


ape = input("¿Me puedes dar tu apellido por favor? ")
print("Gracias.")
print("\nTu nombre es " + nom + " " + ape + ".")

Nota: El utilizar  +  para concatenar cadenas te permite construir la salida de una


manera más precisa, en comparación de utilizar únicamente la función  print() , aún
cuando se enriquezca con los argumentos  end=  y  sep= .
Ejecuta el código y comprueba si la salida es igual a tus predicciones.

Replicación
El signo de  *  (asterisco), cuando es aplicado a una cadena y a un número (o a un
número y cadena) se convierte en un operador de replicación.

cadena * número

número * cadena

Replica la cadena el numero de veces indicado por el número.

Por ejemplo:

 "James" * 3  nos da  "JamesJamesJames" .


 3 * "an"  nos da  "ananan" .
 5 * "2"  (o  "2" * 5 ) da como resultado  "22222"  (no  10 ).

RECUERDA

Un número menor o igual que cero produce una cadena vacía.

Este sencillo programa "dibuja" un rectángulo, haciendo uso del operador ( + ), pero en
un nuevo rol:

print("+" + 10 * "-" + "+")

print(("|" + " " * 10 + "|\n") * 5, end="")

print("+" + 10 * "-" + "+")

Nota como se ha utilizado el paréntesis en la segunda línea de código.

¡Intenta practicar para crear otras figuras o tus propias obras de arte!

Conversión de tipos de datos: str()


A estas alturas ya sabes como emplear las funciones  int()  y  float()  para convertir
una cadena a un número.
Este tipo de conversión no es en un solo sentido. También se puede convertir un
numero a una cadena, lo cual es más fácil y rápido, esta operación es posible hacerla
siempre.

Una función capaz de hacer esto se llama  str() :

str(número)

Sinceramente, puede hacer mucho más que transformar números en cadenas, eso lo
veremos después.

El "triángulo rectángulo" de nuevo


Este es el programa del "triángulo rectángulo" visto anteriormente:

cateto_a = float(input("Ingresa la longitud del primer cateto: "))


cateto_b = float(input("Ingresa la longitud del segundo cateto: "))
print("La longitud de la hipotenusa es: " + str((cateto_a**2 +
cateto_b**2) ** .5))

Se ha modificado un poco para mostrar cómo es que la función  str()  trabaja. Gracias
a esto, podemos pasar el resultado entero a la función  print()  como una sola
cadena, sin utilizar las comas.

Has hecho algunos pasos importantes en tu camino hacia la programación de Python.

Ya conoces los tipos de datos básicos y un conjunto de operadores fundamentales.


Sabes cómo organizar la salida y cómo obtener datos del usuario. Estos son
fundamentos muy sólidos para el Módulo 3. Pero antes de pasar al siguiente módulo,
hagamos unos cuantos laboratorios y resumamos todo lo que has aprendido en esta
sección.

LABORATORIO

Tiempo Estimado
5-10 minutos
Nivel de Dificultad
Fácil

Objetivos
 Familiarizarse con la entrada y salida de datos en Python.
 Evaluar expresiones simples.

Escenario
La tarea es completar el código para evaluar y mostrar el resultado de cuatro
operaciones aritméticas básicas.

El resultado debe ser mostrado en consola.

Quizá no podrás proteger el código de un usuario que intente dividir entre cero. Por
ahora, no hay que preocuparse por ello.

Prueba tu código - ¿Produce los resultados esperados?

# ingresa un valor flotante para la variable a aquí

a=25.3

# ingresa un valor flotante para la variable b aquí

b=6.0

# muestra el resultado de la suma aquí

print(a+b)

# muestra el resultado de la resta aquí

print(a-b)

# muestra el resultado de la multiplicación aquí

print(a*b)

# muestra el resultado de la división aquí

print(a/b)

print("\n¡Eso es todo, amigos!")

LABORATORIO
Tiempo estimado
20 minutos

Nivel de dificultad
Intermedio

Objetivos
 Familiarizarse con los conceptos de números, operadores y expresiones
aritméticas en Python.
 Comprender la precedencia y asociatividad de los operadores de Python, así
como el correcto uso de los paréntesis.

Escenario
La tarea es completar el código para poder evaluar la siguiente expresión:

El resultado debe de ser asignado a  y . Se cauteloso, observa los operadores y


priorízalos. Utiliza cuantos paréntesis sean necesarios.

Puedes utilizar variables adicionales para acortar la expresión (sin embargo, no es muy
necesario). Prueba tu código cuidadosamente.

x = float(input("Ingresa el valor para x: "))

# coloca tu código aquí

y=1/(x+(1/(x+(1/(x+(1/x))))))
print("y =", y)
LABORATORIO

Tiempo estimado
15-20 minutos

Nivel de dificultad
Fácil

Objetivos
 Mejorar la habilidad de implementar números, operadores y operaciones
aritméticas en Python.
 Utilizar la función  print()  y sus capacidades de formateo.
 Aprender a expresar fenómenos del día a día en términos de un lenguaje de
programación.

Escenario
La tarea es preparar un código simple para evaluar o encontrar el tiempo final de un
periodo de tiempo dado, expresándolo en horas y minutos. Las horas van de 0 a 23 y
los minutes de 0 a 59. El resultado debe ser mostrado en la consola.

Por ejemplo, si el evento comienza a las 12:17 y dura 59 minutos, terminará a


las 13:16.

No te preocupes si tu código no es perfecto, está bien si acepta una hora invalida, lo


más importante es que el código produzca una salida correcta acorde a la entrada
dada.

Prueba el código cuidadosamente. Pista: utilizar el operador  %  puede ser clave para el
éxito.

Datos de Prueba
Entrada de muestra:12

17

59

Salida esperada:  13:16


Entrada de muestra:23

58

642

Salida esperada:  10:40

Entrada de muestra:0

2939

Salida esperada:  1:0

hora = int(input("Hora de inicio (horas): "))

min = int(input("Minuto de inicio (minutos): "))

dura = int(input("Duración del evento (minutos): "))

# coloca tu código aqui

minutos=(min+dura)%60

horas=int(hora+(min+dura)/60)%24

print(horas,":",minutos)

RESUMEN DE SECCIÓN

Puntos Clave

1. La función  print()  envía datos a la consola, mientras que la función  input()  obtiene datos
de la consola.
2. La función  input()  viene con un parámetro inicial: un mensaje de tipo cadena para el
usuario. Permite escribir un mensaje antes de la entrada del usuario, por ejemplo:

nombre = input("Ingresa tu nombre: ")


print("Hola, " + nombre + ". ¡Un gusto conocerte!")

3. Cuando la función  input()  es llamada o invocada, el flujo del programa se detiene, el símbolo
del cursor se mantiene parpadeando (le está indicando al usuario que tome acción ya que la consola
está en modo de entrada) hasta que el usuario haya ingresado un dato y/o haya presionado la
tecla Enter.

NOTA

Puedes probar la funcionalidad completa de la función  input()  localmente en tu máquina. Por


razones de optimización, se ha limitado el máximo número de ejecuciones en Edube a solo algunos
segundos únicamente. Ve a Sandbox, copia y pega el código que está arriba, ejecuta el programa y
espera unos segundos. Tu programa debe detenerse después de unos segundos. Ahora abre IDLE, y
ejecuta el mismo programa ahí -¿Puedes notar alguna diferencia?

Consejo: La característica mencionada anteriormente de la función  input()  puede ser utilizada


para pedirle al usuario que termine o finalice el programa. Observa el siguiente código:

nombre = input("Ingresa tu nombre: ")


print("Hola, " + nombre + ". ¡Un gusto conocerte!")

print("\nPresiona la tecla Enter para finalizar el programa.")


input()
print("FIN.")

3. El resultado de la función  input()  es una cadena. Se pueden unir cadenas unas con otras a
través del operador de concatenación ( + ). Observa el siguiente código:

num1 = input("Ingresa el primer número: ") # Ingresa 12


num2 = input("Ingresa el segundo número: ") # Ingresa 21

print(num1 + num2) # el programa regresa 1221

4. También se pueden multiplicar ( *  - replicación) cadenas, por ejemplo:

miEntrada = ("Ingresa Algo: ") # Ejemplo: hola


print(miEntrada * 3) # Salida esperada: holaholahola

Ejercicio 1

¿Cuál es la salida del siguiente código?


x = int(input("Ingresa un número: ")) # el usuario ingresa un 2
print(x * "5")
Revisar

55

Ejercicio 2

¿Cuál es la salida esperada del siguiente código?

x = input("Ingresa un número: ") # el usuario ingresa un 2


print(type(x))
Revisar

<class 'str'>

 3.1.1.2 Tomando decisiones en Python

Preguntas y respuestas

Un programador escribe un programa y el programa hace preguntas.

Una computadora ejecuta el programa y proporciona las respuestas. El programa debe ser capaz
de reaccionar de acuerdo con las respuestas recibidas.

Afortunadamente, las computadoras solo conocen dos tipos de respuestas:

 Si, es cierto.
 No, esto es falso.

Nunca obtendrás una respuesta como Déjame pensar ..., no lo sé, o probablemente sí, pero no lo sé
con seguridad.

Para hacer preguntas, Python utiliza un conjunto de operadores muy especiales. Revisemos
uno tras otro, ilustrando sus efectos en algunos ejemplos simples.

Comparación: operador de igualdad


Pregunta: ¿Son dos valores iguales?

Para hacer esta pregunta, se utiliza el  ==  Operador (igual igual).

No olvides esta importante distinción:

 =  es un operador de asignación, por ejemplo,  a = b  assigna a la varable  a  el valor de  b .
 ==  es una pregunta ¿Son estos valores iguales?;  a == b  compara  a  y  b .

Es un operador binario con enlazado a la izquierda. Necesita dos argumentos y verifica si son
iguales.

Ejercicios

Ahora vamos a hacer algunas preguntas. Intenta adivinar las respuestas.

Pregunta #1: ¿Cuál es el resultado de la siguiente comparación?

2 == 2     Revisar

True (verdadero)  - por supuesto, 2 es igual a 2. Python responderá  True  (Recuerda este par
de literales predefinidos,  True  y  False  - También son palabras clave de Python).

Pregunta # 2: ¿Cuál es el resultado de la siguiente comparación?

2 == 2.     Revisar

Esta pregunta no es tan fácil como la primera. Por suerte, Python es capaz de convertir el valor
entero en su equivalente real, y en consecuencia, la respuesta es  True

Pregunta # 3: ¿Cuál es el resultado de la siguiente comparación?

1 == 2     Revisar

Esto debería ser fácil. La respuesta será (o mejor dicho, siempre es)  False .

 3.1.1.3 Tomando decisiones en Python


Igualdad: El operador igual a (==)
El operador  ==  (igual a) compara los valores de dos operandos. Si son iguales, el
resultado de la comparación es  True . Si no son iguales, el resultado de la comparación
es  False .

Observa la comparación de igualdad a continuación: ¿Cuál es el resultado de esta


operación?

var == 0

Ten en cuenta que no podemos encontrar la respuesta si no sabemos qué valor está
almacenado actualmente en la variable  (var) .

Si la variable se ha cambiado muchas veces durante la ejecución del programa, o si se


ingresa su valor inicial desde la consola, Python solo puede responder a esta pregunta
en el tiempo de ejecución del programa.

Ahora imagina a un programador que sufre de insomnio, y tiene que contar las ovejas
negras y blancas por separado siempre y cuando haya exactamente el doble de ovejas
negras que de las blancas.

La pregunta será la siguiente:

ovejasNegras == 2 * ovejasBlancas

Debido a la baja prioridad de el operador  ==  ,la pregunta será tratada como la


siguiente:

ovejasNegras == (2 * ovejaBlancas)

Entonces, vamos a practicar la comprensión del operador  ==  - ¿Puedes adivinar la


salida del código a continuación?

var = 0 # asignando 0 a var

print(var == 0)

var = 1 # asignando 1 a var

print(var == 0)

Ejecuta el código y comprueba si tenías razón.


Desigualdad: el operador no es igual a (!=)
El operador  !=  (no es igual a) también compara los valores de dos operandos. Aquí
está la diferencia: si son iguales, el resultado de la comparación es  False . Si no son
iguales, el resultado de la comparación es  True .

Ahora echa un vistazo a la comparación de desigualdad a continuación: ¿Puedes


adivinar el resultado de esta operación?

var = 0 # asignando 0 a var

print(var != 0)

var = 1 # asignando 1 a var

print(var != 0)

Ejecuta el código y comprueba si tenías razón.

var = 0 # asignando 0 a var

print(var != 0)

var = 1 # asignando 1 a var

print(var != 0)
False
True

Operadores de Comparación: Mayor que

También se puede hacer una pregunta de comparación usando el operador  >  (mayor que).

Si deseas saber si hay más ovejas negras que blancas, puedes escribirlo de la siguiente manera:

ovejasNegras > ovejasBlancas # mayor que.

True  lo confirma;  False  lo niega.


Operadores de Comparación: Mayor o igual que

El operador mayor que tiene otra variante especial, una variante no estricta, pero se denota de
manera diferente que la notación aritmética clásica:  >=  (mayor o igual que).

Hay dos signos subsecuentes, no uno.

Ambos operadores (estrictos y no estrictos), así como los otros dos que se analizan en la siguiente
sección, son operadores binarios con enlace en el lado izquierdo, y su prioridad es mayor que la
mostrada por  ==  y  != .

Si queremos saber si tenemos que usar un gorro o no, nos hacemos la siguiente pregunta:

centigradosAfuera ≥ 0.0 # mayor o igual a.

Operadores de Comparación: Menor o igual que

Como probablemente ya hayas adivinado, los operadores utilizados en este caso son: El
operador  <  (menor que) y su hermano no estricto:  <=  (menor o igual que).

Mira este ejemplo simple:

velocidadMph < 85 # menor que.

velocidadMph ≤ 85 # menor o igual que.

Vamos a comprobar si existe un riesgo de ser multados (la primera pregunta es estricta, la segunda
no).

Aprovechando las respuestas

¿Qué puedes hacer con la respuesta (es decir, el resultado de una operación de comparación) que se
obtiene de la computadora?

Hay al menos dos posibilidades: primero, puedes memorizarlo (almacenarlo en una variable) y
utilizarlo más tarde. ¿Cómo haces eso? Bueno, utilizarías una variable arbitraria como esta:

respuesta = numerodeLeones >= numerodeLeonas


El contenido de la variable te dirá la respuesta a la pregunta.

La segunda posibilidad es más conveniente y mucho más común: puedes utilizar la respuesta que
obtengas para tomar una decisión sobre el futuro del programa.

Necesitas una instrucción especial para este propósito, y la discutiremos muy pronto.

Ahora necesitamos actualizar nuestra tabla de prioridades , y poner todos los nuevos operadores en
ella. Ahora se ve como a continuación:

Prioridad Operador

1 + ,  - unario

2 **

3 * ,  / ,  %

4 + ,  - binario

5 < ,  <= ,  > ,  >=

6 == ,  !=

LABORATORIO

Tiempo Estimado
5 minutos

Nivel de dificultad
Muy Fácil

Objetivos
 Familiarizarse con la función  input() .
 Familiarizarse con los operadores de comparación en Python.

Escenario
Usando uno de los operadores de comparación en Python, escribe un programa
simple de dos líneas que tome el parámetro  n  como entrada, que es un entero, e
imprime  False  si  n  es menor que  100 , y  True  si  n  es mayor o igual que  100 .
No debes crear ningún bloque  if  (hablaremos de ellos muy pronto). Prueba tu código
usando los datos que te proporcionamos.

Datos de prueba

Ejemplo de entrada:  55

Resultado esperado:  False

Ejemplo de entrada:  99

Resultado esperado:  False

Ejemplo de entrada:  100

Resultado esperado:  True

Ejemplo de entrada:  101

Resultado esperado:  True

Ejemplo de entrada:  -5

Resultado esperado:  False

Ejemplo de entrada:  +123

Resultado esperado:  True

n=int(input(“Introduce el número:”))

Print (n>=100)

Condiciones y ejecución condicional


Ya sabes como hacer preguntas a Python, pero aún no sabes como hacer un uso razonable de las
respuestas. Se debe tener un mecanismo que le permita hacer algo si se cumple una condición, y no
hacerlo si no se cumple.

Es como en la vida real: haces ciertas cosas o no cuando se cumple una condición específica, por
ejemplo, sales a caminar si el clima es bueno, o te quedas en casa si está húmedo y frío.

Para tomar tales decisiones, Python ofrece una instrucción especial. Debido a su naturaleza y su
aplicación, se denomina instrucción condicional (o declaración condicional).

Existen varias variantes de la misma. Comenzaremos con la más simple, aumentando la dificultad
lentamente.

La primera forma de una declaración condicional, que puede ver a continuación, está escrita de
manera muy informal pero figurada:

if cierto_o_no:
hacer_esto_si_cierto

Esta declaración condicional consta de los siguientes elementos, estrictamente necesarios en este
orden:

 La palabra clave  if .
 Uno o más espacios en blanco.
 Una expresión (una pregunta o una respuesta) cuyo valor se interpretar únicamente en
términos de  True  (cuando su valor no sea cero) y  False  (cuando sea igual a cero).
 Unos dos puntos seguido de una nueva línea.
 Una instrucción con sangría o un conjunto de instrucciones (se requiere absolutamente al
menos una instrucción); la sangría se puede lograr de dos maneras: insertando un número
particular de espacios (la recomendación es usar cuatro espacios de sangría), o usando
el tabulador; nota: si hay mas de una instrucción en la parte con sangría, la sangría debe ser
la misma en todas las líneas; aunque puede parecer lo mismo si se mezclan tabuladores con
espacios, es importante que todas las sangrías sean exactamente iguales Python 3 no
permite mezclar espacios y tabuladores para la sangría.

¿Cómo funciona esta declaración?

 Si la expresión  cierto_o_no  representa la verdad (es decir, su valor no es igual a


cero),la(s) declaración(es) con sangría se ejecutará.
 Si la expresión  cierto_o_no  no representa la verdad (es decir, su valor es igual a
cero), las declaraciones con sangría se omitirá , y la siguiente instrucción ejecutada será la
siguiente al nivel de la sangría original.

En la vida real, a menudo expresamos un deseo:


if el clima es bueno, saldremos a caminar

después, almorzaremos

Como puedes ver, almorzar no es una actividad condicional y no depende del clima.

Sabiendo que condiciones influyen en nuestro comportamiento y asumiendo que tenemos las
funciones sin parámetros  irACaminar()  y  almorzar() , podemos escribir el siguiente
fragmento de código:

if ClimaEsBueno:
irAcaminar()
almorzar()

Ejecución condicional: La declaración if


Si un determinado desarrollador de Python sin dormir se queda dormido cuando cuenta 120 ovejas, y
el procedimiento de inducción del sueño se puede implementar como una función especial
llamada  dormirSoñar() , todo el código toma la siguiente forma:

if contadordeOvejas >= 120: #evalúa una expresión de prueba.


dormirSoñar() #se ejecuta si la expresión de prueba es
Verdadera.

Puedes leerlo como sigue: si  contadorOvejas  es mayor o igual que  120 , entonces duerme y
sueña (es decir, ejecuta la función  duermeSueña .)

Hemos dicho que las declaraciones condicionales deben tener sangría. Esto crea una estructura
muy legible, demostrando claramente todas las rutas de ejecución posibles en el código.

Echa un vistazo al siguiente código:

if contadorOvejas >= 120:


hacerCama()
tomarDucha()
dormirSoñar()
alimentarPerros()

Como puedes ver, tender la cama, tomar una ducha y dormir y soñar se ejecutan condicionalmente,
cuando  contadorOvejas  alcanza el límite deseado.

Alimentar a los perros, sin embargo, siempre se hace (es decir, la función  alimentarPerros  no
tiene sangría y no pertenece al bloque  if , lo que significa que siempre se ejecuta).
Ahora vamos a discutir otra variante de la declaración condicional, que también permite realizar una
acción adicional cuando no se cumple la condición.

Ejecución condicional: la declaración if-else


Comenzamos con una frase simple que decÃŒa: Si el clima es bueno, saldremos a caminar.

Nota: no hay una palabra sobre lo que suceder· si el clima es malo. Solo sabemos que no saldremos
al aire libre, pero no sabemos que podríamos hacer. Es posible que también queramos planificar algo
en caso de mal tiempo.

Podemos decir, por ejemplo: Si el clima es bueno, saldremos a caminar, de lo contrario, iremos al
cine.

Ahora sabemos lo que haremos si se cumplen las condiciones , y sabemos lo que haremos si no
todo sale como queremos . En otras palabras, tenemos un "Plan B".

Python nos permite expresar dichos planes alternativos. Esto se hace con una segunda forma,
ligeramente mas compleja, de la declaración condicional, la declaración if-else  :

if condición_true_or_false:
ejecuta_si_condición_true
else:
ejecuta_si_condición_false

Por lo tanto, hay una nueva palabra:  else  - esta es una palabra reservada.

La parte del código que comienza con  else  dice que hacer si no se cumple la condición
especificada por el  if  (observa los dos puntos después de la palabra).

La ejecución de if-else es la siguiente:

 Si la condición se evalúa como Verdadero (su valor no es igual a cero), la


instrucción  ejecuta_si_condición_true  se ejecuta, y la declaración condicional
llega a su fin.
 Si la condición se evalúa como Falso (es igual a cero), la
instrucción  ejecuta_si_condición_false  se ejecuta, y la declaración condicional
llega a su fin.

La declaración if-else: más de ejecución


condicional
Al utilizar esta forma de declaración condicional, podemos describir nuestros planes de la siguiente
manera:

if climaEsBueno:
irACaminar()

else:

irAlCine()

almorzar()

Si el clima es bueno, saldremos a caminar. De lo contrario, iremos al cine. No importa si el clima es


bueno o malo, almorzaremos después (después de la caminata o después de ir al cine).

Todo lo que hemos dicho sobre la sangría funciona de la misma manera dentro de la rama else :

if climaEsBueno:

irACaminar()

Diviertirse()

else:

irAlCine()

disfrutaLaPelicula()

almorzar()

Declaraciones if-else anidadas
Ahora, analicemos dos casos especiales de la declaración condicional.

Primero, considera el caso donde la instrucción colocada después del  if  es otro  if .

Lee lo que hemos planeado para este domingo. Si hay buen clima, saldremos a caminar. Si
encontramos un buen restaurante, almorzaremos allí. De lo contrario, vamos a comer un sandwich.
Si hay mal clima, iremos al cine. Si no hay boletos, iremos de compras al centro comercial más
cercano.

Escribamos lo mismo en Python. Considera cuidadosamente el código aquí:

if climaEsBueno:

if encontramosBuenRestaurante:

almorzar()

else:

comerSandwich()

else:

if hayBoletosDisponibles:

irAlCine()
else:

irDeCompras()

Aquí hay dos puntos importantes:

 Este uso de la declaración if  se conoce como anidamiento; recuerda que cada  else  se
refiere al  if  que se encuentra en el mismo nivel de sangría; se necesita saber esto para
determinar cómo se relacionan los ifs y los  elses.
 Considera como la sangría mejora la legibilidad y hace que el código sea más fácil de
entender y rastrear.

La declaración elif
El segundo caso especial presenta otra nueva palabra clave de Python: elif. Como probablemente
sospechas, es una forma más corta de else-if.

elif  se usa para verificar más de una condición, y para detener cuando se encuentra la primera
declaración verdadera.

Nuestro siguiente ejemplo se parece a la anidación, pero las similitudes son muy leves. Nuevamente,
cambiaremos nuestros planes y los expresaremos de la siguiente manera: si hay buen clima,
saldremos a caminar, de lo contrario, si obtenemos entradas, iremos al cine, de lo contrario, si hay
mesas libres en el restaurante, vamos a almorzar; si todo falla, regresaremos a casa y jugaremos
ajedrez.

¿Has notado cuantas veces hemos usado la palabra de lo contrario? Esta es la etapa en la que la
palabra clave  elif  desempeña su función.

Escribamos el mismo escenario usando Python:

if climaBueno:

iraCaminar()

elif hayBoletosDisponibles:

IralCine()

elif mesasLibres:

almorzar()

else:

jugarAjedrezEnCasa()

La forma de ensamblar las siguientes declaraciones if-elif-else a veces se denomina cascada.

Observa de nuevo como la sangría mejora la legibilidad del código.


Se debe prestar atención adicional a este caso:

 No debes usar  else  sin un  if  precedente.


 Else  siempre es la última rama de la cascada , independientemente de si has
usado  elif  o no.
 Else  es una parte opcional de la cascada, y puede omitirse.
 Si hay una rama  else  en la cascada, solo se ejecuta una de todas las ramas.
 Si no hay una rama  else , es posible que no se ejecute ninguna de las opciones disponibles.

Esto puede sonar un poco desconcertante, pero ojalá que algunos ejemplos simples ayuden a
comprenderlo mejor.

Analizando ejemplos de código


Ahora te mostraremos algunos programas simples pero completos. No los
explicaremos en detalle, porque consideramos que los comentarios (y los nombres de
las variables) dentro del código son guías suficientes.

Todos los programas resuelven el mismo problema: encuentran el número mayor y


lo imprimen.

Ejemplo 1:

Comenzaremos con el caso más simple: ¿Cómo identificar el mayor de los dos


números? :

#lee dos números

numero1 = int (input("Ingresa el primer número:"))

numero2 = int (input("Ingresa el segundo número:"))

#elegir el número más grande

if numero1> numero2:

nmasGrande = numero1

else:

nmasGrande = numero2

#imprimir el resultado

print("El número más grande es:", nmasGrande)


El fragmento de código anterior debe estar claro: lee dos valores enteros, los compara
y encuentra cuál es el más grande.

Ejemplo 2:

Ahora vamos a mostrarte un hecho intrigante. Python tiene una característica


interesante, mira el código a continuación:

#lee dos números

numero1 = int (input("Ingresa el primer número:"))

numero2 = int (input("Ingresa el segundo número:"))

# elegir el número más grande

if numero1 > numero2: nmasGrande = numero1

else: nmasGrande = numero2

#imprimir el resultado

print("El número más grande es: ", nmasGrande)

Nota: si alguna de las ramas de if-elif-else  contiene una sola instrucción, puedes
codificarla de forma más completa (no es necesario que aparezca una línea con
sangría después de la palabra clave), pero solo continúa la línea después de los dos
puntos).

Sin embargo, este estilo puede ser engañoso, y no lo vamos a usar en nuestros
programas futuros, pero definitivamente vale la pena saber si quieres leer y entender
los programas de otra persona.

No hay otras diferencias en el código.

Ejemplo 3:

Es hora de complicar el código: encontremos el mayor de los tres números. ¿Se


ampliará el código? Un poco.

Suponemos que el primer valor es el más grande. Luego verificamos esta hipótesis con
los dos valores restantes.

Observa el siguiente código:


#lee tres números

numero1 = int (input("Ingresa el primer número:"))

numero2 = int (input("Ingresa el segundo número:"))

numero3 = int (input("Ingresa el tercer número:"))

#asumimos temporalmente que el primer número

#es el más grande

#lo verificaremos pronto

nmasGrande = numero1

#comprobamos si el segundo número es más grande que el mayor número


actual

#y actualiza el número más grande si es necesario

if numero2 > nmasGrande:

    nmasGrande = numero2

#comprobamos si el tercer número es más grande que el mayor número


actual

#y actualiza el número más grande si es necesario

if numero3 > nmasGrande:

    nmasGrande = numero3

#imprimir el resultado

print("El número más grande es:", nmasGrande)

Este método es significativamente más simple que tratar de encontrar el número más
grande comparando todos los pares de números posibles (es decir, el primero con el
segundo, el segundo con el tercero y el tercero con el primero). Intenta reconstruir el
código por ti mismo.

Pseudocódigo e introducción a los ciclos o bucles


Ahora deberías poder escribir un programa que encuentre el mayor de cuatro, cinco, seis o incluso
diez números.
Ya conoces el esquema, por lo que ampliar el tamaño del problema no será particularmente
complejo.

¿Pero qué sucede si te pedimos que escribas un programa que encuentre el mayor de doscientos
números? ¿Te imaginas el código?

Necesitarás doscientas variables. Si doscientas variables no son lo suficientemente complicadas,


intenta imaginar la búsqueda del número más grande de un millón.

Imagina un código que contiene 199 declaraciones condicionales y doscientas invocaciones de la


función  input() . Por suerte, no necesitas lidiar con eso. Hay un enfoque más simple.

Por ahora ignoraremos los requisitos de la sintaxis de Python e intentaremos analizar el problema sin
pensar en la programación real. En otras palabras, intentaremos escribir el algoritmo, y cuando
estemos contentos con él, lo implementaremos.

En este caso, utilizaremos un tipo de notación que no es un lenguaje de programación real (no se
puede compilar ni ejecutar), pero está formalizado, es conciso y se puede leer. Se
llama pseudocódigo.

Veamos nuestro pseudocódigo a continuación:

línea 01 numeroMayor = -999999999

línea 02 numero = int(input())

línea 03 if numero == -1:

línea 04 print(numeroMayor)

línea 05 exit()

línea 06 if numero > numeroMayor:

línea 07 numeroMayor = numero

línea 08 vaya a la línea 02

¿Qué está pasando en él?


En primer lugar, podemos simplificar el programa si, al comienzo del código, asignamos la
variable  numeroMayor  con un valor que será más pequeño que cualquiera de los números
ingresados. Usaremos  -999999999  para ese propósito.

En segundo lugar, asumimos que nuestro algoritmo no sabrá por adelantado cuántos números se
entregarán al programa. Esperamos que el usuario ingrese todos los números que desee; el algoritmo
funcionará bien con cien y con mil números. ¿Cómo hacemos eso?

Hacemos un trato con el usuario: cuando se ingresa el valor -1 , será una señal de que no hay más
datos y que el programa debe finalizar su trabajo.

De lo contrario, si el valor ingresado no es igual a  -1 , el programa leerá otro número, y así


sucesivamente.

El truco se basa en la suposición de que cualquier parte del código se puede realizar más de una vez,
precisamente, tantas veces como sea necesario.

La ejecución de una determinada parte del código más de una vez se denomina bucle. El significado
de este término es probablemente obvio para ti.

Las líneas  02  a  08  forman un bucle. Los pasaremos tantas veces como sea necesario para revisar
todos los valores ingresados.

¿Puedes usar una estructura similar en un programa escrito en Python? Si, si puedes.

Información Adicional

Python a menudo viene con muchas funciones integradas que harán el trabajo por ti. Por ejemplo,
para encontrar el número más grande de todos, puede usar una función incorporada de Python
llamada  max() . Puedes usarlo con múltiples argumentos. Analiza el código de abajo:

# lee tres números

numero1 = int(input("Ingresa el primer número:"))

numero2 = int(input("Ingresa el segundo número:"))

numero3 = int(input("Ingresa el tercer número:"))

# verifica cuál de los números es el mayor

# y pásalo a la variable de mayor número


numeroMayor = max(numero1,numero2,numero3)

# imprimir el resultado

print("El número más grande es:", numeroMayor)

De la misma manera, puedes usar la función  min()  para devolver el número más bajo. Puedes
reconstruir el código anterior y experimentar con él en el Sandbox.

Vamos a hablar sobre estas (y muchas otras) funciones pronto. Por el momento, nuestro enfoque se
centrará en la ejecución condicional y los bucles para permitirte ganar más confianza en la
programación y enseñarte las habilidades que te permitirán comprender y aplicar los dos conceptos
en tu codigo. Entonces, por ahora, no estamos tomando atajos.

LABORATORIO

Tiempo estimado
5-10 minutos

Nivel de dificultad
Fácil

Objetivos
 Familiarizarse con la función input().
 Familiarizarse con los operadores de comparación en Python.
 Familiarizarse con el concepto de ejecución condicional.

Escenario
Espatifilo, más comúnmente conocida como la planta de Cuna de Moisés o flor de la
paz, es una de las plantas para interiores más populares que filtra las toxinas dañinas
del aire. Algunas de las toxinas que neutraliza incluyen benceno, formaldehído y
amoníaco.

Imagina que tu programa de computadora ama estas plantas. Cada vez que recibe una
entrada en forma de la palabra  Espatifilo , grita involuntariamente a la consola la
siguiente cadena:  "¡Espatifilo es la mejor planta de todas!"
Escribe un programa que utilice el concepto de ejecución condicional, tome una
cadena como entrada y que:

 Imprima el enunciado  "Si, ¡El Espatifilo es la mejor planta de


todos los tiempos!"  en la pantalla si la cadena ingresada
es  "Espatifilo" .
 Imprima  "No, ¡quiero un gran Espatifilo!"  si la cadena ingresada
es  "espatifilo".
 Imprima  "¡Espatifilo! ¡No [entrada]!"  de lo contrario.
Nota:  [entrada]  es la cadena que se toma como entrada.

Prueba tu código con los datos que te proporcionamos. ¡Y hazte de un Espatifilo


también!

Datos de prueba
Entrada de muestra:  espatifilo

Resultado esperado:  No, ¡quiero un gran Espatifilo!

Entrada de ejemplo:  pelargonio

Resultado esperado:  !Espatifilo! ¡No pelargonio!

Entrada de muestra:  Espatifilo

Resultado esperado:  Si, ¡El Espatifilo es la mejor planta de todos los


tiempos!

nombre=input(“Escribe el nombre de la flor:”)


if nombre==”Espatifilo”:
print (“Si, ¡El Espatifilo es la mejor planta de todos los
tiempos!”)

elif nombre==”espatifilo”:
print(“No,¡quiero un gran Espatifilo!”)
else:
print(“¡Espatifilo! ¡No”, nombrem “!”)
LABORATORIO

Tiempo estimado
10-15 minutos

Nivel de dificultad
Fácil/Medio

Objetivos
Familiarizar al estudiante con:

 Utilizar la instrucción if-else  para ramificar la ruta de control.


 Construir un programa completo que resuelva problemas simples de la vida
real.

Escenario
Érase una vez una tierra - una tierra de leche y miel, habitada por gente feliz y
próspera. La gente pagaba impuestos, por supuesto, su felicidad tenía límites. El
impuesto más importante, denominado Impuesto Personal de Ingresos  (IPI, para
abreviar) tenía que pagarse una vez al año y se evaluó utilizando la siguiente regla:

 Si el ingreso del ciudadano no era superior a 85,528 pesos, el impuesto era


igual al 18% del ingreso menos 556 pesos y 2 centavos (esta fue la
llamada exención fiscal  ).
 Si el ingreso era superior a esta cantidad, el impuesto era igual a 14,839 pesos
y 2 centavos, más el 32% del excedente sobre 85,528 pesos.

Tu tarea es escribir una calculadora de impuestos.

 Debe aceptar un valor de punto flotante: el ingreso.


 A continuación, debe imprimir el impuesto calculado, redondeado a pesos
totales. Hay una función llamada  round()  que hará el redondeo por ti, la
encontrarás en el código de esqueleto del editor.

Nota: Este país feliz nunca devuelve dinero a sus ciudadanos. Si el impuesto calculado
es menor que cero, solo significa que no hay impuesto (el impuesto es igual a cero).
Ten esto en cuenta durante tus cálculos.

Observa el código en el editor: solo lee un valor de entrada y genera un resultado, por
lo que debes completarlo con algunos cálculos inteligentes.
Prueba tu código con los datos que hemos proporcionado.

Datos de prueba
Entrada de muestra:  10000

Resultado esperado:  El impuesto es: 1244.0 pesos

Entrada de muestra:  100000

Resultado esperado:  El impuesto es: 19470.0 pesos

Entrada de muestra:  1000

Resultado esperado:  El impuesto es: 0.0 pesos

Entrada de muestra:  -100

Resultado esperado:  El impuesto es: 0.0 pesos

ingreso=float(input(“Ingrese el ingreso anual:”))


if ingreso<85528:
impuesto=0.18*ingreso – 556.02
else:
impuesto=14839.02+0.32*(ingreso-85528)

impuesto=round(impuesto, 0)

if impuesto<0:

impuesto=0.0

print(“El impuesto es:”, impuesto,”pesos”)

LABORATORIO

Tiempo estimado
10-15 minutos
Nivel de dificultad
Fácil/Medio

Objetivos
Familiarizar al estudiante con:

 Utilizar la declaración if-elif-else.
 Encontrar la implementación adecuada de reglas definidas verbalmente.
 Emplear el código de prueba usando entrada y salida de muestra.

Escenario
Como seguramente sabrás, debido a algunas razones astronómicas, el año pueden
ser bisiesto  o común  . Los primeros tienen una duración de 366 días, mientras que los
últimos tienen una duración de 365 días.

Desde la introducción del calendario gregoriano (en 1582), se utiliza la siguiente regla
para determinar el tipo de año:

 Si el número del año no es divisible entre cuatro, es un año común.


 De lo contrario, si el número del año no es divisible entre 100, es un año
bisiesto.
 De lo contrario, si el número del año no es divisible entre 400, es un año común.
 De lo contrario, es un año bisiesto.

Observa el código en el editor: solo lee un número de año y debe completarse con las
instrucciones que implementan la prueba que acabamos de describir.

El código debe mostrar uno de los dos mensajes posibles, que son  Año
bisiesto  o  Año común , según el valor ingresado.

Sería bueno verificar si el año ingresado cae en la era gregoriana y emitir una
advertencia de lo contrario:  No dentro del período del calendario gregoriano .
Consejo: utiliza los operadores  !=  y  % .

Prueba tu código con los datos que hemos proporcionado.

Datos de prueba
Entrada de muestra:  2000

Resultado esperado:  Año bisiesto


Entrada de muestra:  2015

Resultado esperado:  Año común

Entrada de muestra:  1999

Resultado esperado:  Año común

Entrada de muestra:  1996

Resultado esperado:  Año bisiesto

Entrada de muestra:  1580

Resultado esperado:  No dentro del período del calendario gregoriano

año=int(input(“Introduzca un año:”))

if año<1582:

print(“No dentro del período del calendario gregoriano”)

else:

if año % 4 ¡=0:

print(“Año común”)

elif año %100 !=0:

print(“Año bisiesto”)

elif año %400 !=0:

print(“Año común”)

else:

print(“Año bisiesto”)

RESUMEN DE LA SECCIÓN

Puntos clave
1. Los operadores de comparación (o también denominados relacionales) se utilizan para comparar
valores. La siguiente tabla ilustra cómo funcionan los operadores de comparación, asumiendo
que  x=0 ,  y=1  y  z=0 :

Operador Descripción Ejem


x ==
== Devuelve si los valores de los operandos son iguales, y  False  de lo contrario. x ==

!=
Devuelve  True  si los valores de los operandos no son iguales, y  False  de lo x !=
contrario. x !=
Devuelve True  si el valor del operando izquierdo es mayor que el valor del x >
>
operando derecho, y  False  de lo contrario. y >
Devuelve  True  si el valor del operando izquierdo es menor que el valor del
< x <
operando derecho, y  False  de lo contrario.
x >=
Devuelve  True  si el valor del operando izquierdo es mayor o igual al valor del
≥ x >=
operando derecho, y  False  de lo contrario. y >=
Devuelve  True  si el valor del operando izquierdo es menor o igual al valor del x <=

operando derecho, y  False  de lo contrario. y <=

2. Cuando desea ejecutar algún código solo si se cumple una determinada condición, puede usar
una declaración condicional:

 Una única declaración  if , por ejemplo:

x = 10

if x == 10: # condición
    print("x es igual a 10") # ejecutado si la condición es
verdadera

 Una serie de declaraciones  if , por ejemplo:

x = 10

if x > 5: # condición uno


    print("x es mayor que 5") # ejecutado si la condición uno
es verdadera

if x <10: # condición dos


    print("x es menor que 10") # ejecutado si la condición
dos es verdadera

if x == 10: # condición tres


     print("x es igual a 10") # ejecutado si la condición
tres es verdadera

Cada declaración  if  se prueba por separado.


 Una declaración de  if-else , por ejemplo:

x = 10

if x < 10: # condición


    print ("x es menor que 10") # ejecutado si la condición
es Verdadera

else:
    print ("x es mayor o igual a 10") # ejecutado si la
condición es False

 Una serie de declaraciones  if  seguidas de un  else , por ejemplo:

x = 10

if x > 5: # Verdadero
    print("x > 5")

if x > 8: # Verdadero
    print("x > 8")

if x > 10: # Falso


    print("x > 10")

else:
    print("Se ejecutará el else")

Cada  if  se prueba por separado. El cuerpo de  else  se ejecuta si el último  if  es  False .

 La declaración  if-elif-else , por ejemplo:

x = 10

if x == 10: # Verdadero
    print("x == 10")

if x > 15: # Falso


    print("x > 15")

elif x > 10: # Falso


    print("x > 10")

elif x > 5: # Verdadero


    print("x > 5")

else:
    print("No se ejecutará el else")

Si la condición para  if  es  False , el programa verifica las condiciones de los


bloques  elif  posteriores: el primer  elif  que sea  True  es el que se ejecuta. Si todas las
condiciones son  False , se ejecutará el bloque  else .

 Declaraciones condicionales anidadas, ejemplo:

x = 10

if x > 5: # Verdadero
    if x == 6: # Falso
        print("anidado: x == 6")
    elif x == 10: # Verdadero
        print("anidado: x == 10")
    else:
        print("anidado: else")
else:
    print("else")

Puntos Clave: Continuación

Ejercicio 1

¿Cuál es la salida del siguiente fragmento de código?

x = 5

y = 10

z = 8

print(x > y)

print(y > z)
Revisar

False

True

Ejercicio 2
¿Cuál es la salida del siguiente fragmento de código?

x, y, z = 5, 10, 8

print(x > z)

print((y - 5) == x)
Revisar

False

True

Ejercicio 3

¿Cuál es la salida del siguiente fragmento de código?

x, y, z = 5, 10, 8

x, y, z = z, y, x

print(x > z)

print((y - 5) == x)
Revisar

True

False

Ejercicio 4

¿Cuál es la salida del siguiente fragmento de código?

x = 10

if x == 10:

    print(x == 10)

if x > 5:

    print(x > 5)
if x < 10:

    print(x < 10)

else:

    print("else")
Revisar

True

True

else

Ejercicio 5

¿Cuál es la salida del siguiente fragmento de código?

x = "1"

if x == 1:

    print("uno")

elif x == "1":

    if int (x)> 1:

        print("dos")

    elif int (x) < 1:

        print("tres")

    else:

        print("cuatro")

if int (x) == 1:

    print("cinco")

else:

    print("seis")
Revisar

cuatro
cinco

Ejercicio 6

¿Cuál es la salida del siguiente fragmento de código?

x = 1

y = 1.0

z = "1"

if x == y:

    print("uno")

if y == int (z):

    print("dos")

elif x == y:

    print("tres")

else:

    print("cuatro")
Revisar

uno

dos

 3.1.2.1 Ciclos en Python | while

Ciclos o bucles en el código con while


¿Estás de acuerdo con la declaración presentada a continuación?

mientras haya algo que hacer hazlo

Ten en cuenta que este registro también declara que, si no hay nada que hacer, nada ocurrirá.

En general, en Python, un ciclo se puede representar de la siguiente manera:

while expresión_condicional:
instrucción

Si observas algunas similitudes con la instrucción if, está bien. De hecho, la diferencia sintáctica es
solo una: usa la palabra  while  en lugar de la palabra  if .

La diferencia semántica es más importante: cuando se cumple la condición, if  realiza sus


declaraciones sólo una vez; while repite la ejecución siempre que la condición se evalúe
como  True .

Nota: todas las reglas relacionadas con sangría también se aplican aquí. Te mostraremos esto
pronto.

Observa el algoritmo a continuación:

while expresión_condicional:

instrucción_uno

instruccion_dos

instrucción_tres

instrucción_n

Ahora, es importante recordar que:

 Si deseas ejecutar más de una declaración dentro de un  while , debes (como


con  if ) poner sangría a todas las instrucciones de la misma manera.
 Una instrucción o conjunto de instrucciones ejecutadas dentro del  while  se llama
el cuerpo del ciclo.
 Si la condición es  False  (igual a cero) tan pronto como se compruebe por primera vez, el
cuerpo no se ejecuta ni una sola vez (ten en cuenta la analogía de no tener que hacer nada si
no hay nada que hacer).
 El cuerpo debe poder cambiar el valor de la condición, porque si la condición es  True  al
principio, el cuerpo podría funcionar continuamente hasta el infinito. Observa que hacer una
cosa generalmente disminuye la cantidad de cosas por hacer.

Un bucle o ciclo infinito


Un ciclo infinito, también denominado ciclo sin fin, es una secuencia de instrucciones en un
programa que se repite indefinidamente (ciclo sin fin).

Este es un ejemplo de un ciclo que no puede finalizar su ejecución:

while True:
print("Estoy atrapado dentro de un ciclo")

Este ciclo imprimirá infinitamente  "Estoy atrapado dentro de un ciclo".  En la


pantalla.

Si deseas obtener la mejor experiencia de aprendizaje al ver cómo se comporta un ciclo infinito,
inicia IDLE, cree un Nuevo archivo, copia y pega el código anterior, guarda tu archivo y ejecuta el
programa. Lo que verás es la secuencia interminable de cadenas impresas de  "Estoy atrapado
dentro de un ciclo".  En la ventana de la consola de Python. Para finalizar tu programa,
simplemente presiona Ctrl-C (o Ctrl-Break en algunas computadoras). Esto provocará la
excepción  KeyboardInterrupt  y permitirá que tu programa salga del ciclo. Hablaremos de ello
más adelante en el curso.

Volvamos al bosquejo del algoritmo que te mostramos recientemente. Te mostraremos como usar
este ciclo recién aprendido para encontrar el número más grande de un gran conjunto de datos
ingresados.

Analiza el programa cuidadosamente. Localiza el cuerpo del ciclo y descubre como se sale del
cuerpo:

# Almacenaremos el número más grande actual aquí

numero Mayor = -999999999

# Ingresa el primer valor

numero = int(input ("Introduzca un número o escriba -1 para


detener:"))

# Si el número no es igual a -1, continuaremos

while numero != -1:

# ¿Es el número más grande que el número más grande?

if numero > numeroMayor:

# Sí si, actualiza el mayor númeroNúmero

numeroMayor = numero

# Ingresa el siguiente número

numero = int (input("Introduce un número o escribe -1 para


detener:"))

# Imprimir el número más grande


print("El número más grande es:", numeroMayor)

Comprueba como este código implementa el algoritmo que te mostramos anteriormente.

El ciclo(bucle)  while : más ejemplos

Veamos otro ejemplo utilizando el ciclo  while . Sigue los comentarios para descubrir la
idea y la solución.

# programa que lee una secuencia de números

# y cuenta cuántos números son pares y cuántos son impares

# programa termina cuando se ingresa cero

numerosImpares = 0

numerosPares = 0

# lee el primer número

numero = int (input ("Introduce un número o escriba 0 para


detener:"))

# 0 termina la ejecución

while numero != 0:

# verificar si el número es impar

if numero % 2 == 1:

# aumentar el contador de números impares

numerosImpares += 1

else:

# aumentar el contador de números pares

numerosPares += 1

# lee el siguiente número

numero = int (input ("Introduce un número o escriba 0 para


detener:"))
# imprimir resultados

print("Números impares: ", numerosImpares)

print("Números pares: ", numerosPares)

Ciertas expresiones se pueden simplificar sin cambiar el comportamiento del


programa.

Intenta recordar cómo Python interpreta la verdad de una condición y ten en cuenta
que estas dos formas son equivalentes:

while numero != 0:  y  while numero:

La condición que verifica si un número es impar también puede codificarse en estas


formas equivalentes:

if numero % 2 == 1:  e  if numero % 2:

Usando una variable contador para salir de un ciclo


Observa el fragmento de código a continuación:

contador = 5

while contador != 0:

print("Dentro del ciclo: ", contador)

contador -= 1

print("Fuera del ciclo", contador)

Este código está destinado a imprimir la cadena  "Dentro del ciclo"  y el valor
almacenado en la variable  contador  durante un ciclo dado exactamente cinco veces.
Una vez que la condición se haya cumplido (la variable  contador  ha alcanzado  0 ), se
sale del ciclo y aparece el mensaje  "Fuera del ciclo".  así como el valor
almacenado en  contador  se imprime.
Pero hay una cosa que se puede escribir de forma más compacta: la condición del
ciclo  while .

¿Puedes ver la diferencia?

contador=5

while contador:

print("Dentro del ciclo.", contador)

contador - = 1

print("Fuera del ciclo", contador)

¿Es más compacto que antes? Un poco. ¿Es más legible? Eso es discutible.

RECUERDA

No te sientas obligado a codificar tus programas de una manera que siempre sea la
más corta y la más compacta. La legibilidad puede ser un factor más importante.
Manten tu código listo para un nuevo programador.

LABORATORIO

Tiempo estimado
15 minutos

Nivel de dificultad
Fácil

Objetivos
Familiarizar al estudiante con:

 Utilizar el ciclo while.
 Reflejar situaciones de la vida real en código de computadora.

Escenario
Un mago junior ha elegido un número secreto. Lo ha escondido en una variable
llamada  númeroSecreto . Quiere que todos los que ejecutan su programa jueguen el
juego Adivina el número secreto, y adivina qué número ha elegido para ellos. ¡Quienes
no adivinen el número quedarán atrapados en un ciclo sin fin para siempre!
Desafortunadamente, él no sabe cómo completar el código.

Tu tarea es ayudar al mago a completar el código en el editor de tal manera que el


código:

 Pedirá al usuario que ingrese un número entero.


 Utilizará un ciclo  while .
 Comprobará si el número ingresado por el usuario es el mismo que el número
escogido por el mago. Si el número elegido por el usuario es diferente al
número secreto del mago, el usuario debería ver el mensaje  "¡Ja, ja!
¡Estás atrapado en mi ciclo!"  y se le solicitará que ingrese un número
nuevamente. Si el número ingresado por el usuario coincide con el número
escogido por el mago, el número debe imprimirse en la pantalla, y el mago
debe decir las siguientes palabras:  "¡Bien hecho, muggle! Eres libre
ahora" .

¡El mago está contando contigo! No lo decepciones.

INFO EXTRA

Por cierto, mira la función  print() . La forma en que lo hemos utilizado aquí se
llama impresión multilínea  . Puede utilizar comillas triples para imprimir cadenas en
varias líneas para facilitar la lectura del texto o crear un diseño especial basado en
texto. Experimenta con ello.

numeroSecreto = 777

print(
"""
+==================================+
| Bienvenido a mi juego, muggle! |
| Introduce un número entero |
| y adivina qué número he |
| elegido para ti. |
| Entonces, |
| ¿Cuál es el número secreto? |
+==================================+
""")

numero=int(input(“¿Cuál es el numero secreto?”))


while numero !=numeroSecreto:
print(“¡Ja, ja! ¡Estás atrapado en mi ciclo!”)
int(input(“¿Cuál es el numero secreto?”))
print(“¡Bien hecho, muggle! Eres libre ahora”)

Ciclos(bucles) en el código con  for


Otro tipo de ciclo disponible en Python proviene de la observación de que a veces es más
importante contar los "giros o vueltas" del ciclo que verificar las condiciones.

Imagina que el cuerpo de un ciclo debe ejecutarse exactamente cien veces. Si deseas utilizar el
ciclo  while  para hacerlo, puede tener este aspecto:

i = 0
while i < 100:
# hacer_algo()
i += 1

Sería bueno si alguien pudiera hacer esta cuenta aburrida por ti. ¿Es eso posible?

Por supuesto que lo es, hay un ciclo especial para este tipo de tareas, y se llama  for .

En realidad, el ciclo  for  está diseñado para realizar tareas más complicadas, puede "explorar"
grandes colecciones de datos elemento por elemento. Te mostraremos como hacerlo pronto, pero
ahora presentaremos una variante más sencilla de su aplicación.

Echa un vistazo al fragmento:

for i in range (100):


#hacer algo()
pass

Hay algunos elementos nuevos. Déjanos contarte sobre ellos:

 La palabra reservada for  abre el ciclo  for ; nota - No hay condición después de eso; no
tienes que pensar en las condiciones, ya que se verifican internamente, sin ninguna
intervención.
 Cualquier variable después de la palabra reservada for es la variable de control del ciclo;
cuenta los giros del ciclo y lo hace automáticamente.
 La palabra reservada in introduce un elemento de sintaxis que describe el rango de valores
posibles que se asignan a la variable de control.
 La función  range()  (esta es una función muy especial) es responsable de generar todos
los valores deseados de la variable de control; en nuestro ejemplo, la función creará (incluso
podemos decir que alimentará el ciclo con) valores subsiguientes del siguiente conjunto: 0,
1, 2 .. 97, 98, 99; nota: en este caso, la función  range()  comienza su trabajo desde 0 y lo
finaliza un paso (un número entero) antes del valor de su argumento.
 Nota la palabra clave pass dentro del cuerpo del ciclo - no hace nada en absoluto; es
una instrucción vacía : la colocamos aquí porque la sintaxis del ciclo  for  exige al menos
una instrucción dentro del cuerpo (por cierto,  if ,  elif ,  else  y  while  expresan lo
mismo).

Nuestros próximos ejemplos serán un poco más modestos en el número de repeticiones de ciclo.

Echa un vistazo al fragmento de abajo. ¿Puedes predecir su salida?

for i in range(10):
print("El valor de i es actualmente", i)

Ejecuta el código para verificar si tenías razón.

Nota:

 El ciclo se ha ejecutado diez veces (es el argumento de la función  range() ).


 El valor de la última variable de control es  9  (no  10 , ya que comienza desde  0  , no
desde  1 ).

La invocación de la función  range()  puede estar equipada con dos argumentos, no solo uno:

for i in range (2, 8):


print("El valor de i es actualmente", i)

En este caso, el primer argumento determina el valor inicial (primero) de la variable de control.

El último argumento muestra el primer valor que no se asignará a la variable de control.

Nota: la función  range()  solo acepta enteros como argumentos y genera secuencias de enteros.

¿Puedes adivinar la salida del programa? Ejecútalo para comprobar si ahora también estabas en lo
cierto.

El primer valor mostrado es  2  (tomado del primer argumento de  range() ).

El último es  7  (aunque el segundo argumento de  range()  es  8 ).

Más sobre el ciclo for y la función range() con tres


argumentos
La función  range()  también puede aceptar tres argumentos: Echa un vistazo al
código del editor.

El tercer argumento es un incremento: es un valor agregado para controlar la variable


en cada giro del ciclo (como puedes sospechar, el valor predeterminado del
incremento es 1 ).

¿Puedes decirnos cuántas líneas aparecerán en la consola y qué valores contendrán?

Ejecuta el programa para averiguar si tenías razón.

Deberías poder ver las siguientes líneas en la ventana de la consola:

El valor de i es actualmente 2

El valor de i es actualmente 5

¿Sabes por qué? El primer argumento pasado a la función  range()  nos dice cual es el
número de inicio de la secuencia (por lo tanto,  2  en la salida). El segundo argumento
le dice a la función dónde detener la secuencia (la función genera números hasta el
número indicado por el segundo argumento, pero no lo incluye). Finalmente, el tercer
argumento indica el paso, que en realidad significa la diferencia entre cada número en
la secuencia de números generados por la función.

2 (número inicial) →  5  ( 2  incremento por 3 es igual a  5  - el número está dentro del


rango de 2 a 8) →  8  ( 5  incremento por 3 es igual a  8  - el número no está dentro del
rango de 2 a 8, porque el parámetro de parada no está incluido en la secuencia de
números generados por la función).

Nota: si el conjunto generado por la función  range()  está vacío, el ciclo no ejecutará
su cuerpo en absoluto.

Al igual que aquí, no habrá salida:

for i in range(1, 1):

    print("El valor de i es actualmente", i)

Nota: el conjunto generado por  range()  debe ordenarse en un orden ascendente.
No hay forma de forzar el  range()  para crear un conjunto en una forma diferente.
Esto significa que el segundo argumento de  range()  debe ser mayor que el primero.
Por lo tanto, tampoco habrá salida aquí:

for i in range(2, 1):

    print ("El valor de i es actualmente", i)

Echemos un vistazo a un programa corto cuya tarea es escribir algunas de las


primeras potencias de dos:

pow = 1

for exp in range(16):

    print ("2 a la potencia de", exp, "es", pow)

    pow * = 2

La variable  exp  se utiliza como una variable de control para el ciclo e indica el valor
actual del exponente. La propia exponenciación se sustituye multiplicando por dos.
Dado que 2 0 es igual a 1, después 2 × 1 es igual a 21, 2 × 21 es igual a 22, y así
sucesivamente. ¿Cuál es el máximo exponente para el cual nuestro programa aún
imprime el resultado?

Ejecuta el código y verifica si la salida coincide con tus expectativas.

LABORATORIO

Tiempo estimado
5 minutos

Nivel de dificultad
Muy fácil

Objetivos
Familiarizar al estudiante con:

 Utilizar el ciclo for.
 Reflejar situaciones de la vida real en código de computadora.
Escenario
¿Sabes lo que es Mississippi? Bueno, es el nombre de uno de los estados y ríos en los
Estados Unidos. El río Mississippi tiene aproximadamente 2,340 millas de largo, lo que
lo convierte en el segundo río más largo de los Estados Unidos (el más largo es el río
Missouri). ¡Es tan largo que una sola gota de agua necesita 90 días para recorrer toda
su longitud!

La palabra Mississippi  también se usa para un propósito ligeramente diferente:


para contar mississippily (mississippimente).

Si no estás familiarizado con la frase, estamos aquí para explicarte lo que significa: se
utiliza para contar segundos.

La idea detrás de esto es que agregar la palabra Mississippi  a un número al contar los


segundos en voz alta hace que suene más cercano al reloj, y por lo tanto "un
Mississippi, dos Mississippi, tres Mississippi" tomará aproximadamente unos tres
segundos reales de tiempo. A menudo lo usan los niños que juegan al escondite para
asegurarse de que el buscador haga un conteo honesto.

Tu tarea es muy simple aquí: escribe un programa que use un ciclo  for  para "contar
de forma mississippi" hasta cinco. Habiendo contado hasta cinco, el programa debería
imprimir en la pantalla el mensaje final  "¡Listo o no, ahí voy!"

Utiliza el esqueleto que hemos proporcionado en el editor.

INFO EXTRA

Ten en cuenta que el código en el editor contiene dos elementos que pueden no ser
del todo claros en este momento: la declaración  import time  y el método  sleep() .
Vamos a hablar de ellos pronto.

Por el momento, nos gustaría que supieras que hemos importado el módulo  time  y
hemos utilizado el método  sleep()  para suspender la ejecución de cada función
posterior de  print()  dentro del ciclo  for  durante un segundo, de modo que el
mensaje enviado a la consola se parezca a un conteo real. No te preocupes, pronto
aprenderás más sobre módulos y métodos.

Salida esperada
1 Mississippi

2 Mississippi

3 Mississippi

4 Mississippi
5 Mississippi

import time

# Escribe un ciclo for que cuente hasta cinco.


# Cuerpo del ciclo: imprime el número de iteración del ciclo y la palabra
"Mississippi".
# Cuerpo del ciclo - uso: time.sleep (1)

# Escribe una función de impresión con el mensaje final.

import time

for ¡ in range (1,6):


print(¡,”Mississipi”)
time.sleep(1)
print(“¡Listo o no, ahí voy!”)

Las declaraciones break y continue
Hasta ahora, hemos tratado el cuerpo del ciclo como una secuencia indivisible e
inseparable de instrucciones que se realizan completamente en cada giro del ciclo. Sin
embargo, como desarrollador, podrías enfrentar las siguientes opciones:

 Parece que no es necesario continuar el ciclo en su totalidad; se debe abstener


de seguir ejecutando el cuerpo del ciclo e ir más allá.
 Parece que necesitas comenzar el siguiente giro del ciclo sin completar la
ejecución del turno actual.

Python proporciona dos instrucciones especiales para la implementación de estas dos


tareas. Digamos por razones de precisión que su existencia en el lenguaje no es
necesaria: un programador experimentado puede codificar cualquier algoritmo sin
estas instrucciones. Tales adiciones, que no mejoran el poder expresivo del lenguaje,
sino que solo simplifican el trabajo del desarrollador, a veces se denominan dulces
sintácticos o azúcar sintáctica.

Estas dos instrucciones son:

 Break : Sale del ciclo inmediatamente, e incondicionalmente termina la


operación del ciclo; el programa comienza a ejecutar la instrucción más
cercana después del cuerpo del ciclo.
 Continue : Se comporta como si el programa hubiera llegado repentinamente
al final del cuerpo; el siguiente turno se inicia y la expresión de condición se
prueba de inmediato.

Ambas palabras son palabras clave reservadas.

Ahora te mostraremos dos ejemplos simples para ilustrar como funcionan las dos
instrucciones. Mira el código en el editor. Ejecuta el programa y analiza la salida.
Modifica el código y experimenta.

# break - ejemplo

print("La instrucción de ruptura:")


for i in range(1,6):
if i == 3:
break
print("Dentro del ciclo.", i)
print("Fuera del ciclo.")

# continua - ejemplo

print("\nLa instrucción continue:")


for i in range(1,6):
if i == 3:
continue
print("Dentro del ciclo.", i)
print("Fuera del ciclo.")

Las declaraciones break y continue: más ejemplos


Regresemos a nuestro programa que reconoce el más grande entre los números
ingresados. Lo convertiremos dos veces, usando las instrucciones
de  break  y  continue .

Analiza el código y determina como usarías alguno de ellos.

La variante  break  va aquí:

numeroMayor = -99999999

contador = 0

while True:
    numero = int (input ("Ingresa un número o escribe -1 para
finalizar el programa:"))

    if numero == -1:

        break

    contador = 1

    if numero > numeroMayor:

        numeroMayor = numero

if contador != 0:

    print("El número más grande es", numeroMayor)

else:

    print("No ha ingresado ningún número")

Ejecútalo, pruébalo y experimenta con él.

Y ahora la variante  continue :

numeroMayor = -99999999

contador = 0

numero = int (input("Ingresa un número o escribe -1 para finalizar


el programa:"))

while numero != -1:

    if numero == -1:

        continue

    contador = 1

    if numero > numeroMayor:

        numeroMayor = numero

    numero = int (input ("Ingresa un número o escribe -1 para


finalizar el programa:"))
if contador:

    print("El número más grande es", numeroMayor)

else:

    print("No ha ingresado ningún número")

Otra vez: ejecútalo, pruébalo y experimenta con él.

LABORATORIO

Tiempo estimado
10 minutos

Nivel de dificultad
Fácil

Objetivos
Familiarizar al estudiante con:

 Utilizar la instrucción  break  en los ciclos.


 Reflejar situaciones de la vida real en código de computadora.

Escenario
La instrucción  break  se usa para salir/terminar un ciclo.

Diseña un programa que use un ciclo  while  y le pida continuamente al usuario que
ingrese una palabra a menos que ingrese  "chupacabra"  como la palabra de salida
secreta, en cuyo caso el mensaje  "¡Has dejado el ciclo con éxito".  Debe
imprimirse en la pantalla y el ciclo debe terminar.

No imprimas ninguna de las palabras ingresadas por el usuario. Utiliza el concepto de


ejecución condicional y la declaración  break .

while True:
palabra=input(“Ingresa la palabra clave:”)
if palabra==”chupacabra”:
print(“¡Has dejado el ciclo con éxito”)
break

LABORATORIO

Tiempo estimado
10-15 minutos

Nivel de dificultad
Fácil

Objetivos
Familiarizar al estudiante con:

 Utilizar la instrucción  continue  en los ciclos.


 Reflejar situaciones de la vida real en código de computadora.

Escenario
La sentencia  continue  se usa para omitir el bloque actual y avanzar a la siguiente
iteración, sin ejecutar las declaraciones dentro del ciclo.

Se puede usar tanto con while y ciclos for.

Tu tarea aquí es muy especial: ¡Debes diseñar un devorador de vocales! Escribe un


programa que use:

 Un ciclo  for .
 El concepto de ejecución condicional (if-elif-else).
 La declaración  continue .

Tu programa debe:

 Pedir al usuario que ingrese una palabra.


 Utiliza  userWord = userWord.upper()  para convertir la palabra ingresada
por el usuario a mayúsculas; hablaremos sobre los llamados métodos de
cadena y el  upper()  muy pronto, no te preocupes.
 Utiliza la ejecución condicional y la instrucción  continue  para "comer" las
siguientes vocales A  , E  , I  , O  , U  de la palabra ingresada.
 Imprime las letras no consumidas en la pantalla, cada una de ellas en una línea
separada.
Prueba tu programa con los datos que le proporcionamos.

Datos de prueba
Entrada de muestra:  Gregory

Salida esperada:

Entrada de muestra:  abstemious

Salida esperada:

Entrada de muestra:  IOUEA

Salida esperada:

Ninguna, ya que elimina las vocales

# Indicar al usuario que ingrese una palabra


# y asignarlo a la variable userWord.

userWord=input(“Ingrese una palabra:”)


userWord=userWord.upper()
# Completa el cuerpo del ciclo for.
for letra in userWord:
if letra==”A”:
continue
elif letra==”E”:
continue
elif letra==”I”:
continue
elif letra==”O”:
continue
elif letra==”U”:
continue
else:
print(letra)

LABORATORIO

Tiempo estimado
5-10 minutos

Nivel de dificultad
Fácil

Objetivos
Familiarizar al estudiante con:

 Utilizar la instrucción  continue  en los ciclos.


 Modificar y actualizar el código existente.
 Reflejar situaciones de la vida real en código de computadora.

Escenario
Tu tarea aquí es aún más especial que antes: ¡Debes rediseñar el devorador de vocales
(feo) del laboratorio anterior (3.1.2.10) y crear un mejor devorador de vocales (bonito)
mejorado! Escribe un programa que use:

 Un ciclo  for .
 El concepto de ejecución condicional (if-elif-else  ).
 La declaración  continue .
Tu programa debe:

 Pedir al usuario que ingrese una palabra.


 Utilizar  userWord = userWord.upper()  para convertir la palabra ingresada
por el usuario a mayúsculas; hablaremos sobre los llamados métodos de
cadena y el  upper()  muy pronto, no te preocupes.
 Usa la ejecución condicional y la instrucción  continue  para "comer" las
siguientes vocales A  , E  , I  , O  , U  de la palabra ingresada.
 Asigne las letras no consumidas a la variable  palabrasinVocal  e imprime la
variable en la pantalla.

Analiza el código en el editor. Hemos creado  palabrasinVocal  y le hemos asignado


una cadena vacía. Utiliza la operación de concatenación para pedirle a Python que
combine las letras seleccionadas en una cadena más larga durante los siguientes giros
de ciclo, y asignarlo a la variable  palabrasinVocal .

Prueba tu programa con los datos que le proporcionamos.

Datos de prueba
Entrada de muestra:  Gregory

Salida esperada:

GRGRY

Entrada de muestra:  abstemious

Salida esperada:

BSTMS

Entrada de muestra:  IOUEA

Salida esperada:

Ninguna, elimina vocales

palabraSinVocal = ""

# Indicar al usuario que ingrese una palabra


# y asignarlo a la variable userWord

userWord=input(“Ingrese una palabra:”)


userWord=userWord.upper()

# Completa el cuerpo del ciclo.

for letra in userWord:


if letra==”A”:
continue
elif letra==”E”:
continue
elif letra==”I”:
continue
elif letra==”O”:
continue
elif letra==”U”:
continue
palabraSinVocal+=letra

# Imprimir la palabra asignada a palabraSinVocal.

print(palabraSinVocal)

El while y la opción else
Ambos ciclos,  while  y  for , tienen una característica interesante (y rara vez se usa).

Te mostraremos cómo funciona: intenta juzgar por ti mismo si es utilizable.

En otras palabras, trata de convencerte si la función es valiosa y útil, o solo es azúcar


sintáctica.

Echa un vistazo al fragmento en el editor. Hay algo extraño al final: la palabra


clave  else .

Como pudiste haber sospechado, los ciclos también pueden tener la rama  else ,
como los  if .

La rama  else  del ciclo siempre se ejecuta una vez, independientemente de si el


ciclo ha entrado o no en su cuerpo .
¿Puedes adivinar la salida? Ejecuta el programa para comprobar si tenías razón.

Modifica el fragmento un poco para que el ciclo no tenga oportunidad de ejecutar su


cuerpo ni una sola vez:

i = 5

while i < 5:

    print(i)

    i += 1

else:

    print("else:", i)

El estado de  while  es  Falso  al principio, ¿puedes verlo?

Ejecuta y prueba el programa, y verifica si se ha ejecutado o no la rama  else .

El ciclo for y la rama else
Los ciclos  for  se comportan de manera un poco diferente: echa un vistazo al
fragmento en el editor y ejecútalo.

La salida puede ser un poco sorprendente.

La variable  i  conserva su último valor.

Modifica el código un poco para realizar un experimento más.

i = 111

for i in range(2, 1):

print(i)

else:

print("else:", i)

¿Puedes adivinar la salida?


El cuerpo del ciclo no se ejecutará aquí en absoluto. Nota: hemos asignado la
variable  i  antes del ciclo.

Ejecuta el programa y verifica su salida.

Cuando el cuerpo del ciclo no se ejecuta, la variable de control conserva el valor que
tenía antes del ciclo.

Nota: si la variable de control no existe antes de que comience el ciclo, no existirá


cuando la ejecución llegue a la rama  else .

¿Cómo te sientes acerca de esta variante de  else ?

Ahora vamos a informarte sobre otros tipos de variables. Nuestras variables actuales
solo pueden almacenar un valor a la vez, pero hay variables que pueden hacer
mucho más; pueden almacenar tantos valores como desees.

for i in range(5):

print(i)

else:

print("else:", i)

LABORATORIO

Tiempo estimado
20-30 minutos

Nivel de dificultad
Medio

Objetivos
Familiarizar al estudiante con:

 Utilizar el ciclo  while .


 Encontrar la implementación adecuada de reglas definidas verbalmente.
 Reflejar situaciones de la vida real en código de computadora.

Escenario
Escucha esta historia: Un niño y su padre, un programador de computadoras, juegan
con bloques de madera. Están construyendo una pirámide.

Su pirámide es un poco rara, ya que en realidad es una pared en forma de pirámide,


es plana. La pirámide se apila de acuerdo con un principio simple: cada capa inferior
contiene un bloque más que la capa superior.

La figura ilustra la regla utilizada por los constructores:

Tu tarea es escribir un programa que lea la cantidad de bloques que tienen los
constructores, y generar la altura de la pirámide que se puede construir utilizando
estos bloques.

Nota: La altura se mide por el número de capas completas: si los constructores no


tienen la cantidad suficiente de bloques y no pueden completar la siguiente capa,
terminan su trabajo inmediatamente.

Prueba tu código con los datos que hemos proporcionado.

Datos de prueba

Entrada de muestra:  6

Producto esperado:  La altura de la pirámide es: 3


Entrada de muestra:  20

Salida esperada:  La altura de la pirámide es: 5

Entrada de muestra:  1000

Resultado esperado:  La altura de la pirámide es: 44

Entrada de muestra:  2

Salida esperada:  La altura de la pirámide es: 1

bloques=int(input(“Ingrese el número de bloques:”))

altura=0

while bloques > altura:

altura=altura+1

bloques=bloque-altura

print(“La altura de la pirámide:”, altura)

LABORATORIO

Tiempo estimado
20 minutos

Nivel de dificultad
Media

Objetivos
Familiarizar al estudiante con:

 Utilizar el ciclo  while .


 Convertir ciclos definidos verbalmente en código de Python real.

Escenario
En 1937, un matemático alemán llamado Lothar Collatz formuló una hipótesis
intrigante (aún no se ha comprobado) que se puede describir de la siguiente manera:

1. Toma cualquier número entero que no sea negativo y que no sea cero y
asígnale el nombre  c0 .
2. Si es par, evalúa un nuevo  c0  como  c0 ÷ 2 .
3. De lo contrario, si es impar, evalúe un nuevo  c0  como  3 × c0 + 1 .
4. Si  c0 ≠ 1 , salta al punto 2.

La hipótesis dice que, independientemente del valor inicial de  c0 , el valor siempre
tiende a 1.

Por supuesto, es una tarea extremadamente compleja usar una computadora para
probar la hipótesis de cualquier número natural (incluso puede requerir inteligencia
artificial), pero puede usar Python para verificar algunos números individuales. Tal vez
incluso encuentres el que refutaría la hipótesis.

Escribe un programa que lea un número natural y ejecute los pasos anteriores
siempre que  c0  sea diferente de 1. También queremos que cuente los pasos
necesarios para lograr el objetivo. Tu código también debe mostrar todos los valores
intermedios de  c0 .

Sugerencia: la parte más importante del problema es como transformar la idea de


Collatz en un ciclo  while - esta es la clave del éxito.

Prueba tu código con los datos que hemos proporcionado.

Datos de prueba

Entrada de muestra:  15

Salida esperada:

46

23

70

35

106

53
160

80

40

20

10

16

pasos = 17

Entrada de muestra:  16

Salida esperada:

pasos = 4

Entrada de muestra:  1023

Salida esperada:

3070

1535

4606

2303

6910

3455
10366

5183

15550

7775

23326

11663

34990

17495

52486

26243

78730

39365

118096

59048

29524

14762

7381

22144

11072

5536

2768

1384

692

346

173

520

260

130

65

196

98
49

148

74

37

112

56

28

14

22

11

34

17

52

26

13

40

20

10

16

pasos = 62

c0=int(input(“Ingresa un número)

if c0<1:

print(“Ingresa un número que no sea negativo y que no sea


cero”)
exit

pasos=0

while c0 !=1:

if c0 % 2==0:

c0=c0//2

else:

c0=3*c0+1

print(c0)

pasos+=1

print(“pasos:”,pasos)

RESUMEN DE LA SECCIÓN

Puntos clave

1. Hay dos tipos de ciclos en Python:  while  y  for :

 El ciclo  while  ejecuta una sentencia o un conjunto de declaraciones siempre que una
condición booleana especificada sea verdadera, por ejemplo:

# Ejemplo 1
while True:
print("Atascado en un ciclo infinito")

# Ejemplo 2
contador = 5
while contador > 2:
print(contador)
contador -= 1

 El ciclo  for  ejecuta un conjunto de sentencias muchas veces; se usa para iterar sobre una
secuencia (por ejemplo, una lista, un diccionario, una tupla o un conjunto; pronto aprenderás
sobre ellos) u otros objetos que son iterables (por ejemplo, cadenas). Puedes usar el
ciclo  for  para iterar sobre una secuencia de números usando la función
incorporada  range . Mira los ejemplos a continuación:

# Ejemplo 1
palabra = "Python"
for letter in palabra:
print(letter, fin = "*")

# Ejemplo 2
for i in range(1, 10):
if i % 2 == 0:
print(i)

2. Puedes usar las sentencias  break  y  continue  para cambiar el flujo de un ciclo:

 Utiliza  break  para salir de un ciclo, por ejemplo:

texto = "OpenEDG Python Institute"


for letter in texto:
if letter == "P":
break
print(letter, end= "")

 Utiliza  continue  para omitir la iteración actual, y continuar con la siguiente iteración, por
ejemplo:

text = "pyxpyxpyx"
for letter in text:
if letter == "x":
continue
print(letter, end= "")

3. Los ciclos  while  y  for  también pueden tener una cláusula  else  en Python. La
cláusula  else  se ejecuta después de que el ciclo finalice su ejecución siempre y cuando no haya
terminado con  break , por ejemplo:

n = 0

while n != 3:
print(n)
n += 1
else:
print(n, "else")

print()

for i in range(0, 3):


print(i)
else:
print(i, "else")

4. La función  range()  genera una secuencia de números. Acepta enteros y devuelve objetos de
rango. La sintaxis de  range()  tiene el siguiente aspecto:  range(start, stop, step) ,
donde:
 start  es un parámetro opcional que especifica el número de inicio de la secuencia ( 0  por
defecto).
 stop  es un parámetro opcional que especifica el final de la secuencia generada (no está
incluido).
 y  step  es un parámetro opcional que especifica la diferencia entre los números en la
secuencia es ( 1  por defecto).

Código de ejemplo:

for i in range(3):
print(i, end=" ") # salidas: 0 1 2

for i in range(6, 1, -2):


print(i, end=" ") # salidas: 6, 4, 2

Puntos clave: continuación


Ejercicio 1

Crea un bucle  for  que cuente de 0 a 10, e imprima números impares en la pantalla. Usa el esqueleto
de abajo:

for i in range(1, 11):

# línea de código

# línea de código
Revisar

Solución de muestra:
for i in range(0, 11):

if i % 2 != 0:

print(i))

Ejercicio 2

Crea un bucle  while  que cuente de 0 a 10, e imprima números impares en la pantalla. Usa el
esqueleto de abajo:

x = 1

while x < 11:

# line of code

# line of code
# line of code
Revisar

Solución de muestra:
x = 1

while x < 11:

if x % 2 != 0:

print(x)

x += 1

Ejercicio 3

Crea un programa con un bucle  for  y una declaración  break . El programa debe iterar sobre los
caracteres en una dirección de correo electrónico, salir del bucle cuando llegue al símbolo  @  e
imprimir la parte antes de  @  en una línea. Usa el esqueleto de abajo:

for ch in "[email protected]":

if ch == "@":

# línea de código

# línea de código
Revisar

Solución de muestra:
for ch in "[email protected]":

if ch == "@":

break

print(ch, end="")

Ejercicio 4

Crea un programa con un bucle  for  y una declaración continue . El programa debe iterar sobre
una cadena de dígitos, reemplazar cada  0  con  x , e imprimir la cadena modificada en la pantalla. Usa
el esqueleto de abajo:

for digit in "0165031806510":


if digit == "0":

# línea de código

# línea de código

# línea de código
Revisar

Solución de muestra:
for digit in "0165031806510":

if digit == "0":

print("x", end="")

continue

print(digit, end="")

Ejercicio 5

¿Cuál es la salida del siguiente código?

n = 3

while n > 0:

print(n + 1)

n -= 1

else:

print(n)
Revisar

0
Ejercicio 6

¿Cuál es la salida del siguiente código?

n = range(4)

for num in n:

print(num - 1)

else:

print(num)
Revisar

-1

Ejercicio 7

¿Cuál es la salida del siguiente código?

for i in range(0, 6, 3):

print(i)
Revisar

 3.1.3.1 Operaciones lógicas y de bits en Python | and, or, not

Lógica de computadora
¿Te has dado cuenta de que las condiciones que hemos usado hasta ahora han sido muy simples, por
no decir, bastante primitivas? Las condiciones que utilizamos en la vida real son mucho más
complejas. Veamos esta oración:

Si tenemos tiempo libre,  y  el clima es bueno, saldremos a caminar.


Hemos utilizado la conjunción  and (y) , lo que significa que salir a caminar depende del
cumplimiento simultáneo de estas dos condiciones. En el lenguaje de la lógica, tal conexión de
condiciones se denomina conjunción. Y ahora otro ejemplo:

Si estás en el centro comercial  o  estoy en el centro comercial, uno de nosotros le comprará un


regalo a mamá.

La aparición de la palabra  or (o)  significa que la compra depende de al menos una de estas
condiciones. En lógica, este compuesto se llama una disyunción.

Está claro que Python debe tener operadores para construir conjunciones y disyunciones. Sin ellos, el
poder expresivo del lenguaje se debilitaría sustancialmente. Se llaman operadores lógicos.

and
Un operador de conjunción lógica en Python es la palabra y. Es un operador binario con una
prioridad inferior a la expresada por los operadores de comparación. Nos permite codificar
condiciones complejas sin el uso de paréntesis como este:

contador > 0 and valor == 100

El resultado proporcionado por el operador  and  se puede determinar sobre la base de la tabla de
verdad.

Si consideramos la conjunción de  A and B , el conjunto de valores posibles de argumentos y los


valores correspondientes de conjunción se ve de la siguiente manera:

Argumento A Argumento B A y B

False False False

False True False

True False False

True True True

or
Un operador de disyunción es la palabra  or . Es un operador binario con una prioridad más baja
que  and  (al igual que  +  en comparación con  * ). Su tabla de verdad es la siguiente:
Argumento A Argumento B A or B

False False False

False True True

True False True

True True True

not
Además, hay otro operador que se puede aplicar para condiciones de construcción. Es un operador
unario que realiza una negación lógica. Su funcionamiento es simple: convierte la verdad en falso
y lo falso en verdad.

Este operador se escribe como la palabra  not , y su prioridad es muy alta: igual que el
unario  +  y  - . Su tabla de verdad es simple:

Argumento not Argumento

False True

True False

Expresiones lógicas
Creemos una variable llamada  var  y asignémosle  1 . Las siguientes condiciones son equivalentes a
pares:

print(var > 0)

print(not (var <= 0))


print(var != 0)

print(not (var == 0))

Puedes estar familiarizado con las leyes de De Morgan. Dicen que:

La negación de una conjunción es la separación de las negaciones.

La negación de una disyunción es la conjunción de las negaciones.


Escribamos lo mismo usando Python:

not (p and q) == (not p) or (not q)

not (p or q) == (not p) and (not q)

Observa como se han utilizado los paréntesis para codificar las expresiones: las colocamos allí para
mejorar la legibilidad.

Deberíamos agregar que ninguno de estos operadores de dos argumentos se puede usar en la forma
abreviada conocida como  op= . Vale la pena recordar esta excepción.

Valores lógicos vs. bits individuales


Los operadores lógicos toman sus argumentos como un todo, independientemente de cuántos bits
contengan. Los operadores solo conocen el valor: cero (cuando todos los bits se restablecen)
significa  False ; no cero (cuando se establece al menos un bit) significa  True .

El resultado de sus operaciones es uno de estos valores:  False  o  True . Esto significa que este
fragmento de código asignará el valor  True  a la variable  j  si  i  no es cero; de lo contrario,
será  False .

i = 1

j = not not i

Operadores bitwise
Sin embargo, hay cuatro operadores que le permiten manipular bits de datos individuales. Se
denominan operadores bitwise.

Cubren todas las operaciones que mencionamos anteriormente en el contexto lógico, y un operador
adicional. Este es el operador  xor  (como en o exclusivo ), y se denota como  ^  (signo de
intercalación).

Aquí están todos ellos:

 &  (ampersand) - conjunción a nivel de bits.


 |  (barra vertical) - disyunción a nivel de bits.
 ~  (tilde) - negación a nivel de bits.
 ^  (signo de intercalación) - exclusivo a nivel de bits o (xor).

Operaciones bitwise (&, |, y ^)

g B Arg B & Arg B Arg A | Arg B Arg A ^ Arg B


0 0 0

0 1 1

0 1 1

1 1 0

Operaciones bitwise (~)

Arg ~Arg

0 1

1 0

Hagámoslo más fácil:

 &  requieres exactamente dos  1  s para proporcionar  1  como resultado.


 |  requiere al menos un  1  para proporcionar  1  como resultado.
 ^  requiere exactamente un  1  para proporcionar  1  como resultado.

Agreguemos un comentario importante: los argumentos de estos operadores deben ser enteros. No


debemos usar flotantes aquí.

La diferencia en el funcionamiento de los operadores lógicos y de bits es importante: los operadores


lógicos no penetran en el nivel de bits de su argumento. Solo les interesa el valor entero final.

Los operadores bitwise son más estrictos: tratan con cada bit por separado. Si asumimos que la
variable entera ocupa 64 bits (lo que es común en los sistemas informáticos modernos), puede
imaginar la operación a nivel de bits como una evaluación de 64 veces del operador lógico para cada
par de bits de los argumentos. Su analogía es obviamente imperfecta, ya que en el mundo real todas
estas 64 operaciones se realizan al mismo tiempo (simultáneamente).

Operaciones lógicas vs operaciones de bit:


continuación
Ahora te mostraremos un ejemplo de la diferencia en la operación entre las operaciones lógicas y de
bit. Supongamos que se han realizado las siguientes tareas:

i = 15

j = 22
Si asumimos que los enteros se almacenan con 32 bits, la imagen a nivel de bits de las dos variables
será la siguiente:

i: 00000000000000000000000000001111

j: 00000000000000000000000000010110

Se ejecuta la asignación:

og = i and j

Estamos tratando con una conjunción lógica aquí. Vamos a trazar el curso de los cálculos. Ambas
variables  i  y  j  no son ceros, por lo que se considerará que representan a  True . Al consultar la
tabla de verdad para el operador  and , podemos ver que el resultado será  True . No se realizan otras
operaciones.

log: True

Ahora la operación a nivel de bits - aquí está:

bit = i & j

El operador  &  operará con cada par de bits correspondientes por separado, produciendo los valores
de los bits relevantes del resultado. Por lo tanto, el resultado será el siguiente:

i 000000000000000000000000000 01111

j 000000000000000000000000000 10110

bit = i & j 000000000000000000000000000 00110

Estos bits corresponden al valor entero de seis.

Veamos ahora los operadores de negación. Primero el lógico:

logneg = not i

La variable  logneg  se establecerá en  False : no es necesario hacer nada más.

La negación a nivel de bits es así:


bitneg = ~i

Puede ser un poco sorprendente: el valor de la variable  bitneg  es  -16 . Esto puede parecer
extraño, pero no lo es en absoluto. Si deseas obtener más información, debes consultar el sistema de
números binarios y las reglas que rigen los números de complemento de dos.

i 0000000000000000000000000000 1111

bitneg = ~i 1111111111111111111111111111 0000

Cada uno de estos operadores de dos argumentos se puede utilizar en forma abreviada. Estos son
los ejemplos de sus notaciones equivalentes:

x = x & y x &= y

x = x | y x |= y

x = x ^ y x ^= y

¿Cómo tratamos los bits individuales?


Ahora te mostraremos para que puedes usar los operadores de bitwise. Imagina que eres un
desarrollador obligado a escribir una pieza importante de un sistema operativo. Se te ha dicho que
puedes usar una variable asignada de la siguiente forma:

flagRegister = 0x1234

La variable almacena la información sobre varios aspectos de la operación del sistema. Cada bit de
la variable almacena un valor de si/no. También se te ha dicho que solo uno de estos bits es tuyo,
el tercero (recuerda que los bits se numeran desde cero y el número de bits cero es el más bajo,
mientras que el más alto es el número 31). Los bits restantes no pueden cambiar, porque están
destinados a almacenar otros datos. Aquí está tu bit marcado con la letra  x :

flagRegister = 000000000000000000000000000000x000

Es posible que tengas que hacer frente a las siguientes tareas:

1. Comprobar el estado de tu bit: deseas averiguar el valor de su bit; comparar la variable


completa con cero no hará nada, porque los bits restantes pueden tener valores completamente
impredecibles, pero puedes usar la siguiente propiedad de conjunción:

x & 1 = x
x & 0 = 0

Si aplicas la operación  &  a la variable  flagRegister  junto con la siguiente imagen de bits:
00000000000000000000000000001000

(observa el  1  en la posición de tu bit) como resultado, obtendrás una de las siguientes cadenas de
bits:

 00000000000000000000000000001000  si tu bit se estableció en  1


 00000000000000000000000000000000  si tu bit se reseteo a  0

Dicha secuencia de ceros y unos, cuya tarea es tomar el valor o cambiar los bits seleccionados, se
denomina máscara de bits.

Construyamos una máscara de bits para detectar el estado de tus bits. Debería apuntar a el tercer bit.
Ese bit tiene el peso de  23=8 . Se podría crear una máscara adecuada mediante la siguiente
declaración:

theMask = 8

También puedes hacer una secuencia de instrucciones dependiendo del estado de tu bit i, aquí está:

if flagRegister & theMask:


# mi bit está listo
else:
# mi bit se restablece

2. Reinicia tu bit: asigna un cero al bit, mientras que todos los otros bits deben permanecer sin
cambios; usemos la misma propiedad de la conjunción que antes, pero usemos una máscara
ligeramente diferente, exactamente como se muestra a continuación:

11111111111111111111111111110111

Tenga en cuenta que la máscara se creó como resultado de la negación de todos los bits de la
variable  theMask . Restablecer el bit es simple, y se ve así (elige el que más te guste):

flagRegister = flagRegister & ~theMask

flagregister &= ~theMask

3. Establece tu bit : asigna un  1  a tu bit, mientras que todos los bits restantes deben permanecer sin
cambios; usa la siguiente propiedad de disyunción:

x | 1 = 1
x | 0 = x

Ya estás listo para configurar su bit con una de las siguientes instrucciones:
flagRegister = flagRegister | theMask

flagRegister |= theMask

4. Niega tu bit: reemplaza un  1  con un  0  y un  0  con un  1 . Puedes utilizar una propiedad
interesante del operador  ~x :

x ^ 1 = ~x
x ^ 0 = x

Niega tu bit con las siguientes instrucciones:

flagRegister = flagRegister ^ theMask

flagRegister ^= theMask

Desplazamiento izquierdo binario y desplazamiento


derecho binario
Python ofrece otra operación relacionada con los bits individuales: shifting. Esto se
aplica solo a los valores de número entero, y no debe usar flotantes como
argumentos para ello.

Ya aplicas esta operación muy a menudo y muy inconscientemente. ¿Cómo multiplicas


cualquier número por diez? Echa un vistazo:

12345 × 10 = 123450

Como puede ver, multiplicar por diez es de hecho un desplazamiento de todos los


dígitos a la izquierda y llenar el vacío resultante con cero.

¿División entre diez? Echa un vistazo:

12340 ÷ 10 = 1234

Dividir entre diez no es más que desplazar los dígitos a la derecha.

La computadora realiza el mismo tipo de operación, pero con una diferencia: como
dos es la base para los números binarios (no 10), desplazar un valor un bit a la
izquierda corresponde a multiplicarlo por dos ; respectivamente, desplazar un bit
a la derecha es como dividir entre dos (observe que se pierde el bit más a la
derecha).
Los operadores de cambio en Python son un par de dígrafos:  < <  y  > > , sugiriendo
claramente en qué dirección actuará el cambio.

valor << bits


valor >> bits

El argumento izquierdo de estos operadores es un valor entero cuyos bits se


desplazan. El argumento correcto determina el tamaño del turno.

Esto demuestra que esta operación ciertamente no es conmutativa.

La prioridad de estos operadores es muy alta. Los verás en la tabla de prioridades


actualizada, que te mostraremos al final de esta sección.

Echa un vistazo a los cambios en la ventana del editor.

La invocación final de  print()  produce el siguiente resultado:

17 68 8

Nota:

 17 // 2  →  8  (desplazarse hacia la derecha en un bit equivale a la división de


enteros en dos)
 17 * 4  →  68  (desplazarse hacia la izquierda dos bits es lo mismo que
multiplicar números enteros por cuatro).

Y aquí está la tabla de prioridades actualizada , que contiene todos los operadores
presentados hasta ahora:

Prioridad Operador
1 ! ~ (tipo) ++ -- + - unario
2 **
3 * / %
4 + - binario
5 << >>
6 <<=>> =
7 == !=
8 &
9 |
10 &&
11 ||
12 = += -= *= /= %= &= ^= |= >>= <<=

var = 17

varRight = var >> 1

varLeft = var << 2

print(var, varLeft, varRight)

 RESUMEN DE LA SECCIÓN

Puntos clave

1. Python es compatible con los siguientes operadores lógicos:

 and  → si ambos operandos son verdaderos, la condición es verdadera, por ejemplo,  (True
and True)  es  True .
 or  → si alguno de los operandos es verdadero, la condición es verdadera, por
ejemplo,  (True or False)  es  True .
 not  → devuelve False si el resultado es verdadero y devuelve True si es falso, por
ejemplo,  not True  es  False .

2. Puedes utilizar operadores bit a bit para manipular bits de datos individuales. Los siguientes datos
de muestra:

 x = 15 , el cual es  0000 1111  en binario.


 y = 16 , el cual es  0001 0000  en binario.

Se utilizarán para ilustrar el significado de operadores bit a bit en Python. Analiza los ejemplos a
continuación:

 &  hace un bit a bit and (y), por ejemplo,  x & y = 0 , el cual es  0000 0000  en binario.
 |  hace un bit a bit or (o), por ejemplo,  x | y = 31 , el cual es  0001 1111  en binario.
  hace un bit a bit not (no), por ejemplo,  ˜x = 240 , el cual es  1111 0000  en binario.
˜
 ^  hace un bit a bit xor, por ejemplo,  x ^ y = 31 , el cual es  0001 1111  en binario.
 >>  hace un desplazamiento bit a bit a la derecha, por ejemplo,  y >> 1 = 8 , el cual
es  0000 1000  en binario.
 <<  hace un desplazamiento bit a bit a la izquierda, por ejemplo,  y << 3 = , el cual
es  1000 0000  en binario.
Ejercicio 1

¿Cuál es la salida del siguiente fragmento de código?

x = 1
y = 0

z = ((x == y) and (x == y)) or not(x == y)


print(not(z))
Revisar

False

Ejercicio 2

¿Cuál es la salida del siguiente fragmento de código?

x = 4
y = 1

a = x & y
b = x | y
c = ~ x
d = x ^ 5
e = x >> 2
f = x << 2

print(a, b, c, d, e, f)
Revisar

0 5 -5 1 1 16

 3.1.4.1 Listas - colecciones de datos

¿Por qué necesitamos listas?


Puede suceder que tengas que leer, almacenar, procesar y, finalmente, imprimir docenas, quizás
cientos, tal vez incluso miles de números. ¿Entonces que? ¿Necesitas crear una variable separada
para cada valor? ¿Tendrás que pasar largas horas escribiendo declaraciones como la que se muestra a
continuación?

var1 = int(input())
var2 = int(input())
var3 = int(input())
var4 = int(input())
var5 = int(input())
var6 = int(input())
:
:

Si no crees que esta sea una tarea complicada, toma un papel y escribe un programa que:

 Lea cinco números.


 Los imprima en orden desde el más pequeño hasta el más grande (Este tipo de
procesamiento se denomina ordenamiento).

Debes percatarte que ni siquiera tienes suficiente papel para completar la tarea.

Hasta ahora, has aprendido como declarar variables que pueden almacenar exactamente un valor
dado a la vez. Tales variables a veces se denominan escalares por analogía con las matemáticas.
Todas las variables que has usado hasta ahora son realmente escalares.

Piensa en lo conveniente que sería declarar una variable que podría almacenar más de un valor .
Por ejemplo, cien, o mil o incluso diez mil. Todavía sería una y la misma variable, pero muy amplia
y espaciosa. ¿Suena atractivo? Quizás, pero ¿cómo manejarías un contenedor así lleno de valores
diferentes? ¿Cómo elegirías solo el que necesitas?

¿Y si solo pudieras numerarlos? Y luego di: dame el valor número 2; asigna el valor número 15;
aumenta el número del valor 10000.

Te mostraremos como declarar tales variables de múltiples valores . Haremos esto con el ejemplo
que acabamos de sugerir. Escribiremos un programa que ordene una secuencia de números. No
seremos particularmente ambiciosos: asumiremos que hay exactamente cinco números.

Vamos a crear una variable llamada  numeros ; se le asigna no solo un número, sino que se llena con
una lista que consta de cinco valores (nota: la lista comienza con un corchete abierto y termina
con un corchete cerrado ; el espacio entre los corchetes es llenado con cinco números separados
por comas).

numeros = [ 10, 5, 7, 2, 1]

Digamos lo mismo utilizando una terminología adecuada:  numeros  es una lista que consta de
cinco valores, todos ellos números. También podemos decir que esta declaración crea una lista de
longitud igual a cinco (ya que contiene cinco elementos).

Los elementos dentro de una lista pueden tener diferentes tipos . Algunos de ellos pueden ser
enteros, otros son flotantes y otros pueden ser listas.

Python ha adoptado una convención que indica que los elementos de una lista están siempre
numerados desde cero. Esto significa que el elemento almacenado al principio de la lista tendrá el
número cero. Como hay cinco elementos en nuestra lista, al último de ellos se le asigna el número
cuatro. No olvides esto.

Pronto te acostumbrarás y se convertirá en algo natural.


Antes de continuar con nuestra discusión, debemos indicar lo siguiente: nuestra lista es una
colección de elementos, pero cada elemento es un escalar.

 3.1.4.2 Listas - colecciones de datos | Indexación

Listas de indexación
¿Cómo cambias el valor de un elemento elegido en la lista?

Vamos a asignar un nuevo valor de  111  al primer elemento en la lista. Lo hacemos
de esta manera:

numeros = [10, 5, 7, 2, 1]

print("Contenido de la lista original:", numeros) # imprime el


contenido de la lista original

numeros[0] = 111

print("Nuevo contenido de la lista:", numeros) # contenido de la


lista actual.

Y ahora queremos copiar el valor del quinto elemento al segundo elemento.


¿Puedes adivinar como hacerlo?

numeros = [10, 5, 7, 2, 1]

print("Contenido de la lista original:", numeros) # imprimiendo


contenido de la lista original.

numeros[0] = 111

print("\nPrevio contenido de la lista:", numeros) # imprimiendo


contenido de la lista anterior.

numeros[1] = numeros[4] # copiando el valor del quinto elemento al


segundo

print("Nuevo contenido de la lista:", numeros) # imprimiendo el


contenido de la lista actual.
El valor dentro de los corchetes que selecciona un elemento de la lista se llama
un índice, mientras que la operación de seleccionar un elemento de la lista se conoce
como indexación.

Vamos a utilizar la función  print()  para imprimir el contenido de la lista cada vez que
realicemos los cambios. Esto nos ayudará a seguir cada paso con más cuidado y ver
que sucede después de una modificación de la lista en particular.

Nota: todos los índices utilizados hasta ahora son literales. Sus valores se fijan en el
tiempo de ejecución, pero cualquier expresión también puede ser un índice. Esto
abre muchas posibilidades.

Accediendo al contenido de la lista


Se puede acceder a cada uno de los elementos de la lista por separado. Por ejemplo,
se puede imprimir:

print(numeros[0]) # accediendo al primer elemento de la lista.

Suponiendo que todas las operaciones anteriores se hayan completado con éxito, el
fragmento enviará  111  a la consola.

Como puedes ver en el editor, la lista también puede imprimirse como un todo, como
aquí:

print(numeros) # imprimiendo la lista completa.

Como probablemente hayas notado antes, Python decora la salida de una manera que
sugiere que todos los valores presentados forman una lista. La salida del fragmento de
ejemplo anterior se ve así:

[111, 1, 7, 2, 1]

La función len()
La longitud de una lista puede variar durante la ejecución. Se pueden agregar nuevos
elementos a la lista, mientras que otros pueden eliminarse de ella. Esto significa que la
lista es una entidad muy dinámica.

Si deseas verificar la longitud actual de la lista, puedes usar una función


llamada  len()  (su nombre proviene de length - longitud).
La función toma el nombre de la lista como un argumento y devuelve el número de
elementos almacenados actualmente dentro de la lista (en otras palabras, la
longitud de la lista).

Observa la última línea de código en el editor, ejecuta el programa y verifica que valor
imprimirá en la consola. ¿Puedes adivinar?

numeros = [10, 5, 7, 2, 1]
print("Contenido de la lista original:", numeros) # imprimiendo el contenido
de la lista original

numeros [0] = 111


print("\nPrevio contenido de la lista:", numeros) # imprimiendo contenido
de la lista anterior

numeros [1] = numeros [4] # copiando el valor del quinto elemento al


segundo
print("Contenido de la lista anterior:", numeros) # imprimiendo contenido
de la lista anterior

print("\nLongitud de la lista:", len(numeros)) # imprimiendo la longitud de la


lista

Contenido de la lista original: [10, 5, 7, 2, 1]

Previo contenido de la lista: [111, 5, 7, 2, 1]


Contenido de la lista anterior: [111, 1, 7, 2, 1]

Longitud de la lista: 5

 3.1.4.4 Listas - colecciones de datos | Operaciones en listas

Eliminando elementos de una lista


Cualquier elemento de la lista puede ser eliminado en cualquier momento, esto se
hace con una instrucción llamada  del  (eliminar). Nota: es una instrucción, no una
función.

Tienes que apuntar al elemento que quieres eliminar, desaparecerá de la lista y la


longitud de la lista se reducirá en uno.

Mira el fragmento de abajo. ¿Puedes adivinar qué salida producirá? Ejecuta el


programa en el editor y comprueba.

del numeros[1]
print(len(numeros))

print(numeros)

No puedes acceder a un elemento que no existe , no puedes obtener su valor ni


asignarle un valor. Ambas instrucciones causarán ahora errores de tiempo de
ejecución:

print(numeros[4])

numeros[4] = 1

Agrega el fragmento de código anterior después de la última línea de código en el


editor, ejecute el programa y verifique que sucede.

Nota: hemos eliminado uno de los elementos de la lista; ahora solo hay cuatro
elementos en la lista. Esto significa que el elemento número cuatro no existe.

Los índices negativos son válidos


Puede parecer extraño, pero los índices negativos son válidos y pueden ser muy útiles.

Un elemento con un índice igual a  -1  es el último en la lista.

print(numeros[-1])

El código del ejemplo mostrará  1 . Ejecuta el programa y comprueba.

Del mismo modo, el elemento con un índice igual a  -2  es el anterior al último en la
lista.

print(numeros[-2])

El fragmento de ejemplo mostrará  2 .

El último elemento accesible en nuestra lista es  numeros[-4]  (el primero). ¡No
intentes ir más lejos!

LABORATORIO
Tiempo estimado
5 minutos

Nivel de dificultad
Muy fácil

Objetivos
Familiarizar al estudiante con:

 Usar instrucciones básicas relacionadas con listas.


 Crear y modificar listas.

Escenario
Había una vez un sombrero. El sombrero no contenía conejo, sino una lista de cinco
números:  1 ,  2 ,  3 ,  4  y  5 .

Tu tarea es:

 Escribir una línea de código que solicite al usuario que reemplace el número
central en la lista con un número entero ingresado por el usuario (paso 1).
 Escribir una línea de código que elimine el último elemento de la lista (paso 2).
 Escribir una línea de código que imprima la longitud de la lista existente (paso
3).

¿Listo para este desafío?

listaSombrero = [1, 2, 3, 4, 5] # Esta es una lista existente de números


ocultos en el sombrero.

# Paso 1: escribe una línea de código que solicite al usuario


# para reemplazar el número de en medio con un número entero
ingresado por el usuario.
reemplazasombrero=int(input("Reemplaza el sombreo del medio"))
listaSombrero[2]=reemplazasombrero

# Paso 2: escribe aquí una línea de código que elimine el último elemento
de la lista.
del listaSombrero[-1]
# Paso 3: escribe aquí una línea de código que imprima la longitud de la
lista existente.
print("\nLongitud de la lista:", len(listaSombrero))
print(listaSombrero)

 3.1.4.7 Listas - colecciones de datos | Funciones y métodos

Funciones vs. métodos


Un método es un tipo específico de función: se comporta como una función y se parece a una
función, pero difiere en la forma en que actúa y en su estilo de invocación.

Una función no pertenece a ningún dato: obtiene datos, puede crear nuevos datos y (generalmente)
produce un resultado.

Un método hace todas estas cosas, pero también puede cambiar el estado de una entidad
seleccionada.

Un método es propiedad de los datos para los que trabaja, mientras que una función es
propiedad de todo el código.

Esto también significa que invocar un método requiere alguna especificación de los datos a partir de
los cuales se invoca el método.

Puede parecer desconcertante aquí, pero lo trataremos en profundidad cuando profundicemos en la


programación orientada a objetos.

En general, una invocación de función típica puede tener este aspecto:

resultado = funcion(argumento)

La función toma un argumento, hace algo y devuelve un resultado.

Una invocación de un método típico usualmente se ve así:

resultado = data.method(arg)
Nota: el nombre del método está precedido por el nombre de los datos que posee el método. A
continuación, se agrega un punto, seguido del nombre del método y un par de paréntesis que
encierran los argumentos.

El método se comportará como una función, pero puede hacer algo más: puede cambiar el estado
interno de los datos a partir de los cuales se ha invocado.

Puedes preguntar: ¿por qué estamos hablando de métodos, y no de listas?

Este es un tema esencial en este momento, ya que le mostraremos como agregar nuevos elementos a
una lista existente. Esto se puede hacer con métodos propios de las listas, no por funciones.

 3.1.4.8 Listas - colecciones de datos | Métodos de listas

Agregar elementos a una lista: append() e insert()


Un nuevo elemento puede ser añadido al final de la lista existente:

lista.append(valor)

Dicha operación se realiza mediante un método llamado  append() . Toma el valor de


su argumento y lo coloca al final de la lista que posee el método.

La longitud de la lista aumenta en uno.

El método  insert()  es un poco más inteligente: puede agregar un nuevo


elemento en cualquier lugar de la lista, no solo al final.

lista.insert(ubicación,valor)

Toma dos argumentos:

 El primero muestra la ubicación requerida del elemento a insertar. Nota: todos


los elementos existentes que ocupan ubicaciones a la derecha del nuevo
elemento (incluido el que está en la posición indicada) se desplazan a la
derecha, para hacer espacio para el nuevo elemento.
 El segundo es el elemento a insertar.
Observa el código en el editor. Ve como usamos los métodos  append()  e  insert() .
Presta atención a lo que sucede después de usar  insert() : el primer elemento
anterior ahora es el segundo, el segundo el tercero, y así sucesivamente.

Agrega el siguiente fragmento después de la última línea de código en el editor:

numeros.insert(1,333)

Imprime el contenido de la lista final en la pantalla y ve que sucede. El fragmento de


código sobre el fragmento de código inserta  333  en la lista, por lo que es el segundo
elemento. El segundo elemento anterior se convierte en el tercero, el tercero en el
cuarto, y así sucesivamente.

numeros = [111, 7, 2, 1]
print(len(numeros))
print(numeros)

###

numeros.append(4)

print(len(numeros))
print(numeros)

###

numeros.insert(0,222)
print(len(numeros))
print(numeros)

Agregando elementos a una lista: continuación


Puedes iniciar la vida de una lista creándola vacía (esto se hace con un par de
corchetes vacíos) y luego agregar nuevos elementos según sea necesario.

Echa un vistazo al fragmento en el editor. Intenta adivinar su salida después de la


ejecución del bucle  for . Ejecuta el programa para comprobar si tenías razón.

Será una secuencia de números enteros consecutivos del  1  hasta  5 .


Hemos modificado un poco el fragmento:

miLista = [] # creando una lista vacía

for i in range(5):

miLista.insert(0, i + 1)

print(miLista)

¿Qué pasará ahora? Ejecuta el programa y comprueba si esta vez también tenías
razón.

Deberías obtener la misma secuencia, pero en orden inverso (este es el mérito de


usar el método  insert() ).

miLista = [] # creando una lista vacía

for i in range (5):


miLista.append (i + 1)

print(miLista)
[1, 2, 3, 4, 5]

miLista = [] # creando una lista vacía

for i in range(5):
miLista.insert(0, i + 1)

print(miLista)
[5, 4, 3, 2, 1]

 3.1.4.10 Listas - colecciones de datos | listas y bucles

Haciendo uso de las listas


El bucle  for  tiene una variante muy especial que puede procesar las listas de
manera muy efectiva. Echemos un vistazo a eso.

Supongamos que desea calcular la suma de todos los valores almacenados en la


lista  miLista .

Necesitas una variable cuya suma se almacenará y se le asignará inicialmente un valor


de  0  - su nombre es  suma . Luego agrega todos los elementos de la lista usando el
bucle  for . Echa un vistazo al fragmento en el editor.

Comentemos este ejemplo:

 A la lista se le asigna una secuencia de cinco valores enteros.


 La variable  i  toma los valores  0 ,  1 , 2 , 3 , y  4 , y luego indexa la lista,
seleccionando los elementos siguientes: el primero, segundo, tercero, cuarto y
quinto.
 Cada uno de estos elementos se agrega junto con el operador  +=  a la
variable  suma , dando el resultado final al final del bucle.
 Observa la forma en que se ha empleado la función  len() , hace que el código
sea independiente de cualquier posible cambio en el contenido de la lista.

La segunda cara del ciclo for


Pero el bucle  for  puede hacer mucho más. Puede ocultar todas las acciones
conectadas a la indexación de la lista y entregar todos los elementos de la lista de
manera práctica.

Este fragmento modificado muestra como funciona:

miLista = [10, 1, 8, 3, 5]

suma = 0

for i in miLista:

suma += i

print(suma)

¿Qué sucede aquí?


 La instrucción  for  especifica la variable utilizada para navegar por la lista ( i )
seguida de la palabra clave  in  y el nombre de la lista siendo procesado
( miLista ).
 A la variable  i  se le asignan los valores de todos los elementos de la lista
subsiguiente, y el proceso ocurre tantas veces como hay elementos en la lista.
 Esto significa que usa la variable  i  como una copia de los valores de los
elementos, y no necesita emplear índices.
 La función  len()  tampoco es necesaria aquí.

Las listas en acción


Dejemos de lado las listas por un breve momento y veamos un tema intrigante.

Imagina que necesitas reorganizar los elementos de una lista, es decir, revertir el
orden de los elementos: el primero y el quinto, así como el segundo y cuarto
elementos serán intercambiados. El tercero permanecerá intacto.

Pregunta: ¿Cómo se pueden intercambiar los valores de dos variables?

Echa un vistazo al fragmento:

variable1 = 1

variable2 = 2

variable2 = variable1

variable1 = variable2

Si haces algo como esto, perderás el valor previamente almacenadoen variable2 .


Cambiar el orden de las tareas no ayudará. Necesitas una tercera variable que sirva
como almacenamiento auxiliar.

Así es como puedes hacerlo:

variable1 = 1

variable2 = 2

auxiliar = variable1

variable1 = variable2
variable2 = auxiliar

Python ofrece una forma más conveniente de hacer el intercambio, echa un vistazo:

variable1 = 1

variable2 = 2

variable1, variable2 = variable2, variable1

Claro, efectivo y elegante, ¿no?

Listas en acción
Ahora puedes intercambiar fácilmente los elementos de la lista para revertir su
orden:

miLista = [10, 1, 8, 3, 5]

miLista [0], miLista [4] = miLista [4], miLista [0]


miLista [1], miLista [3] = miLista [3], miLista [1]

print(miLista)

Ejecuta el fragmento. Su salida debería verse así:

[5, 3, 8, 1, 10]

Se ve bien con cinco elementos.

¿Seguirá siendo aceptable con una lista que contenga 100 elementos? No, no lo hará.

¿Puedes usar el bucle  for  para hacer lo mismo automáticamente,


independientemente de la longitud de la lista? Si, si puedes.

Así es como lo hemos hecho:

miLista = [10, 1, 8, 3, 5]
longitud = len(miLista)
for i in range (longitud // 2):
miLista[i], miLista[longitud-i-1] = miLista[longitud-i-1],
miLista[i]

print(miLista)

Nota:

 Hemos asignado la variable  longitud  a la longitud de la lista actual (esto hace


que nuestro código sea un poco más claro y más corto).
 Hemos lanzado el ciclo  for  para que se ejecute a través de su
cuerpo  longitud // 2  veces (esto funciona bien para listas con longitudes
pares e impares, porque cuando la lista contiene un número impar de
elementos, el del medio permanece intacto).
 Hemos intercambiado el elemento i (desde el principio de la lista) por el que
tiene un índice igual a  (longitud-i-1)  (desde el final de la lista); en nuestro
ejemplo, for  i  igual a  0  la  (longitud-i-1)  da  4 ; for  i  igual a  3 , da  3 : esto es
exactamente lo que necesitábamos.

Las listas son extremadamente útiles y las encontrarás muy a menudo.

 3.1.4.13 LABORATORIO: Lo básico de las listas - The Beatles

LABORATORIO

Tiempo estimado
10-15 minutos

Nivel de dificultad
Fácil

Objetivos
Familiarizar al estudiante con:

 Crear y modificar listas simples.


 Utilizar métodos para modificar listas.

Escenario
Los Beatles fueron uno de los grupos de música más populares de la década de 1960 y
la banda más vendida en la historia. Algunas personas los consideran el acto más
influyente de la era del rock. De hecho, se incluyeron en la compilación de la
revista Time de las 100 personas más influyentes del siglo XX.

La banda sufrió muchos cambios de formación, que culminaron en 1962 con la


formación de John Lennon, Paul McCartney, George Harrison y Richard Starkey (mejor
conocido como Ringo Starr).

Escribe un programa que refleje estos cambios y le permita practicar con el concepto
de listas. Tu tarea es:

 Paso 1: Crea una lista vacía llamada  beatles .


 Paso 2: Emplea el método  append()  para agregar los siguientes miembros de
la banda a la lista:  John Lennon ,  Paul McCartney  y  George Harrison .
 Paso 3: Emplea el ciclo for  y el  append()  para pedirle al usuario que agregue
los siguientes miembros de la banda a la lista:  Stu Sutcliffe , y  Pete Best .
 Paso 4: Usa la instrucción  del  para eliminar a  Stu Sutcliffe  y  Pete Best  de
la lista.
 Paso 5: Usa el método  insert()  para agregar a  Ringo Starr  al principio de la
lista.

Por cierto, ¿eres fan de los Beatles?

# paso 1
beatles=[]
print("Paso 1:", beatles)

# paso 2
beatles.append("John Lennon")
beatles.append("Paul McCartney")
beatles.append("George Harrison")
print("Paso 2:", beatles)

# paso 3
for miembros in range(2):
beatles.append(input("Nuevo miembro de la banda:"))
print("Paso 3:", beatles)

# etapa 4
del beatles [-1]
del beatles [-1]
print("Paso 4:", beatles)
# paso 5
beatles.insert (0, "Ringo Starr")
print("Paso 5:", beatles)

# probando la longitud de la lista


print("Los Fab", len(beatles))

RESUMEN DE LA SECCIÓN

Puntos clave

1. La lista es un tipo de dato en Python que se utiliza para almacenar múltiples objetos. Es


una colección ordenada y mutable de elementos separados por comas entre corchetes, por ejemplo:

miLista = [1, None, True, "Soy una cadena", 256, 0]

2. Las listas se pueden indexar y actualizar , por ejemplo:

miLista = [1, 1, None, True, 'Soy una cadena', 256, 0]

print(miLista [3]) # salida: soy una cadena

print(miLista [-1]) # salida: 0

miLista [1] = '?'

print (miLista) # salida: [1, '?', True, 'Soy una cadena', 256, 0]

miLista.insert (0, "first")

miLista.append ("last")

print (miLista ) # salida: ['first', 1, '?', True, 'Soy una


cadena', 256, 0, 'last']
3. Las listas pueden estar anidadas, por ejemplo:  miLista = [1, 'a', ["lista", 64,
[0, 1], False]] .

4. Los elementos de la lista y las listas se pueden eliminar, por ejemplo:

miLista = [1, 2, 3, 4]

del miLista[2]

print(miLista) # salida: [1, 2, 4]

del miLista # borra toda la lista

5.Las listas pueden ser iteradas mediante el uso del bucle  for , por ejemplo:

miLista = ["blanco", "purpura", "azul", "amarillo", "verde"]

for color in miLista :

print(color)

6. La función  len()  se puede usar para verificar la longitud de la lista, por ejemplo:

miLista = ["blanco", "purpura", "azul", "amarillo", "verde"]

print(len(miLista)) # la salidas es 5

del miLista[2]

print (len(miLista)) # la salidas es 4

7. Una invocación típica de función tiene el siguiente aspecto:  resultado =


funcion(argumento) , mientras que una invocación típica de un método se ve así:  resultado
= data.method(arg) .

Ejercicio 1

¿Cuál es la salida del siguiente fragmento de código?

lst = [1, 2, 3, 4, 5]
lst.insert(1, 6)

del lst[0]

lst.append(1)

print(lst)

Revisar

[6, 2, 3, 4, 5, 1]

Ejercicio 2

¿Cuál es la salida del siguiente fragmento de código?

lst = [1, 2, 3, 4, 5]

lst2 = []

agregar = 0

for number in lst:

agregar += number

lst2.append (agregar)

print(lst2)

Revisar

[1, 3, 6, 10, 15]

Ejercicio 3

¿Qué sucede cuando ejecutas el siguiente fragmento de código?

lst = []

del lst

print(lst)

Revisar

NameError: el nombre 'lst' no está definido

Ejercicio 4

¿Cuál es la salida del siguiente fragmento de código?


lst = [1, [2, 3], 4]

print(lst[1])

print(len(lst))

Revisar

[2, 3]

 3.1.5.1 Ordenando listas simples - el ordenamiento de burbuja

Ordenamiento Burbuja
Ahora que puedes hacer malabarismos con los elementos de las listas, es hora de aprender
como ordenarlos. Se han inventado muchos algoritmos de clasificación, que difieren mucho en
velocidad, así como en complejidad. Vamos a mostrar un algoritmo muy simple, fácil de entender,
pero desafortunadamente, tampoco es muy eficiente. Se usa muy raramente, y ciertamente no para
listas extensas.

Digamos que una lista se puede ordenar de dos maneras:

 Ascendente (o más precisamente, no descendente): si en cada par de elementos adyacentes,


el primer elemento no es mayor que el segundo.
 Descendente (o más precisamente, no ascendente): si en cada par de elementos adyacentes,
el primer elemento no es menor que el segundo.

En las siguientes secciones, ordenaremos la lista en orden ascendente, de modo que los números se
ordenen de menor a mayor.

Aquí está la lista:

8 10 6 2 4

Intentaremos utilizar el siguiente enfoque: tomaremos el primer y el segundo elemento y los


compararemos; si determinamos que están en el orden incorrecto (es decir, el primero es mayor que
el segundo), los intercambiaremos; Si su orden es válido, no haremos nada. Un vistazo a nuestra lista
confirma lo último: los elementos 01 y 02 están en el orden correcto, así como  8<10 .

Ahora observa el segundo y el tercer elemento. Están en las posiciones equivocadas. Tenemos que
intercambiarlos:

8 6 10 2 4
Vamos más allá y observemos los elementos tercero y cuarto. Una vez más, esto no es lo que se
supone que es. Tenemos que intercambiarlos:

8 6 2 10 4

Ahora comprobemos los elementos cuarto y quinto. Si, ellos también están en las posiciones
equivocadas. Ocurre otro intercambio:

8 6 2 4 10

El primer paso a través de la lista ya está terminado. Todavía estamos lejos de terminar nuestro
trabajo, pero algo curioso ha sucedido mientras tanto. El elemento más grande,  10 , ya ha llegado al
final de la lista. Ten en cuenta que este es el lugar deseado para el. Todos los elementos restantes
forman un lío pintoresco, pero este ya está en su lugar.

Ahora, por un momento, intenta imaginar la lista de una manera ligeramente diferente, es decir, de
esta manera:

10
4
2
6
8

Observa - El  10  está en la parte superior. Podríamos decir que flotó desde el fondo hasta la
superficie, al igual que las burbujas en una copa de champán. El método de clasificación deriva su
nombre de la misma observación: se denomina ordenamiento de burbuja.

Ahora comenzamos con el segundo paso a través de la lista. Miramos el primer y el segundo
elemento, es necesario un intercambio:

6 8 2 4 10

Tiempo para el segundo y tercer elemento: también tenemos que intercambiarlos:

6 2 8 4 10
Ahora el tercer y cuarto elementos, y la segunda pasada, se completa, ya que  8  ya está en su lugar:

6 2 4 8 10

Comenzamos el siguiente pase inmediatamente. Observe atentamente el primer y el segundo


elemento: se necesita otro cambio:

2 6 4 8 10

Ahora  6  necesita ir a su lugar. Cambiamos el segundo y el tercer elemento:

2 4 6 8 10

La lista ya está ordenada. No tenemos nada más que hacer. Esto es exactamente lo que queremos.

Como puedes ver, la esencia de este algoritmo es simple: comparamos los elementos adyacentes y,
al intercambiar algunos de ellos, logramos nuestro objetivo.

Codifiquemos en Python todas las acciones realizadas durante un solo paso a través de la lista, y
luego consideraremos cuántos pases necesitamos para realizarlo. No hemos explicado esto hasta
ahora, pero lo haremos pronto.

Ordenando una lista


¿Cuántos pases necesitamos para ordenar la lista completa?

Resolvamos este problema de la siguiente manera: introducimos otra variable, su


tarea es observar si se ha realizado algún intercambio durante el pase o no. Si no hay
intercambio, entonces la lista ya está ordenada, y no hay que hacer nada más.
Creamos una variable llamada  swapped , y le asignamos un valor de  False  para
indicar que no hay intercambios. De lo contrario, se le asignará  True .

miLista = [8, 10, 6, 2, 4] # lista para ordenar

for i in range(len(miLista) - 1): # necesitamos (5 - 1)


comparaciones

if miLista[i] > miLista[i + 1]: # compara elementos adyacentes

miLista[i], miLista [i + 1] = miLista[i + 1], miLista[i] #


si terminamos aquí significa que tenemos que intercambiar los
elementos.
Deberías poder leer y comprender este programa sin ningún problema:

miLista = [8, 10, 6, 2, 4] # lista para ordenar

swapped = True # lo necesitamos verdadero (True) para ingresar al


bucle while

while swapped:

swapped = False # no hay swaps hasta ahora

for i in range(len(miLista) - 1):

if miLista[i] > miLista[i + 1]:

swapped= True # ocurrió el intercambio!

miLista[i], miLista[i + 1] = miLista[i + 1], miLista[i]

print(miLista)

Ejecuta el programa y pruébalo.

[2, 4, 6, 8, 10]

El ordenamiento burbuja - versión interactiva


En el editor, puedes ver un programa completo, enriquecido por una conversación con
el usuario, y que permite ingresar e imprimir elementos de la lista: El ordenamiento
burbuja: versión interactiva final.

Python, sin embargo, tiene sus propios mecanismos de clasificación. Nadie necesita
escribir sus propias clases, ya que hay un número suficiente de herramientas listas
para usar.

Te explicamos este sistema de clasificación porque es importante aprender como


procesar los contenidos de una lista y mostrarte como puede funcionar la clasificación
real.

Si quieres que Python ordene tu lista, puedes hacerlo de la siguiente manera:

miLista = [8, 10, 6, 2, 4]

miLista.sort()

print(miLista)
Es tan simple como eso.

La salida del fragmento es la siguiente:

[2, 4, 6, 8, 10]

Como puedes ver, todas las listas tienen un método denominado  sort() , que las
ordena lo más rápido posible. Ya has aprendido acerca de algunos de los métodos de
lista anteriormente, y pronto aprenderás más sobre otros.

miLista = []
swapped = True
num = int (input("¿Cuántos elementos deseas ordenar?:"))

for i in range(num):
val = float(input("Introduce un elemento de la lista:"))
miLista.append(val)

while swapped:
swapped = False
for i in range(len(miLista) - 1):
if miLista[i] > miLista[i + 1]:
swapped = True
miLista[i], miLista[i + 1] = miLista[i + 1], miLista[i]

print("\nOrdenado:")
print(miLista)

 RESUMEN DE LA SECCIÓN

Puntos clave

1. Puedes usar el método  sort()  para ordenar los elementos de una lista, por ejemplo:

lst = [5, 3, 1, 2, 4]

print(lst)
lst.sort ()

print(lst) # salida: [1, 2, 3, 4, 5]

2.También hay un método de lista llamado  reverse() , que puedes usar para invertir la lista, por
ejemplo:

lst = [5, 3, 1, 2, 4]

print(lst)

lst.reverse()

print (lst) # salida: [4, 2, 1, 3, 5]

Ejercicio 1

¿Cuál es la salida del siguiente fragmento de código?

lst = ["D", "F", "A", "Z"]

lst.sort ()

print(lst)
Revisar

['A', 'D', 'F', 'Z']

Ejercicio 2

¿Cuál es la salida del siguiente fragmento de código?

a = 3

b = 1

c = 2
lst = [a, c, b]

lst.sort ()

print(lst)
Revisar

[1, 2, 3]

Ejercicio 3

¿Cuál es la salida del siguiente fragmento de código?

a = "A"

b = "B"

c = "C"

d = ""

lst = [a, b, c, d]

lst.reverse ()

print(lst)
Revisar

['', 'C', 'B', 'A']

 3.1.6.1 Operaciones en listas

La vida al interior de las listas


Ahora queremos mostrarte una característica importante y muy sorprendente de las
listas, que las distingue de las variables ordinarias.

Queremos que lo memorices, ya que puede afectar tus programas futuros y causar
graves problemas si se olvida o se pasa por alto.

Echa un vistazo al fragmento en el editor.

El programa:
 Crea una lista de un elemento llamada  lista1 .
 La asigna a una nueva lista llamada  lista2 .
 Cambia el único elemento de  lista1 .
 Imprime la  lista2 .

La parte sorprendente es el hecho de que el programa mostrará como resultado:  [2] ,


no  [1] , que parece ser la solución obvia.

Las listas (y muchas otras entidades complejas de Python) se almacenan de diferentes


maneras que las variables ordinarias (escalares).

Se podría decir que:

 El nombre de una variable ordinaria es el nombre de su contenido.


 El nombre de una lista es el nombre de una ubicación de memoria donde se
almacena la lista.

Lee estas dos líneas una vez más, la diferencia es esencial para comprender de que
vamos a hablar a continuación.

La asignación:  lista2 = lista1 copia el nombre de la matriz, no su contenido. En


efecto, los dos nombres ( lista1  y  lista2 ) identifican la misma ubicación en la
memoria de la computadora. Modificar uno de ellos afecta al otro, y viceversa.

¿Cómo te las arreglas con eso?

lista1 = [1]
lista2 = lista1
lista1[0] = 2
print(lista2)

[2]

Rodajas Poderosas
Afortunadamente, la solución está al alcance de su mano: su nombre es rodaja.

Una rodaja es un elemento de la sintaxis de Python que permite hacer una copia


nueva de una lista, o partes de una lista.

En realidad, copia el contenido de la lista, no el nombre de la lista.


Esto es exactamente lo que necesitas. Echa un vistazo al fragmento de código a
continuación:

lista1 = [1]

lista2 = lista1[:]

lista1[0] = 2

print(lista2)

Su salida es  [1] .

Esta parte no visible del código descrito como  [:]  puede producir una lista
completamente nueva.

Una de las formas más generales de la rodaja es la siguiente:

miLista[inicio:fin]

Como puedes ver, se asemeja a la indexación, pero los dos puntos en el interior hacen
una gran diferencia.

Una rodaja de este tipo crea una nueva lista (de destino), tomando elementos de
la lista de origen: los elementos de los índices desde el principio hasta el  fin-1 .

Nota: no hasta el  fin , sino hasta  fin-1 . Un elemento con un índice igual a  fin  es el
primer elemento el cual no participa en la segmentación.

Es posible utilizar valores negativos tanto para el inicio como para el fin(al igual que en
la indexación).

Echa un vistazo al fragmento:

miLista = [10, 8, 6, 4, 2]

nuevaLista = miLista [1:3]

print(nuevaLista)

La lista  nuevaLista  contendrá  inicio-fin  (3-1=2) elementos, los que tienen índices
iguales a  1  y  2  (pero no  3 )

La salida del fragmento es:  [8, 6]


Rodajas - índices negativos
Observa el fragmento de código a continuación:

miLista[inicio:fin]

Para repetir:

 inicio  es el índice del primer elemento incluido en la rodaja.


 fin  es el índice del primer elemento no incluido en la rodaja.

Así es como los índices negativos funcionan con la rodaja:

miLista = [10, 8, 6, 4, 2]
nuevaLista = miLista [1:-1]
print(nuevaLista)

El resultado del fragmento es:  [8, 6, 4] .

Si el inicio  especifica un elemento que se encuentra más allá del descrito


por  fin  (desde el punto de vista inicial de la lista), la rodaja estará vacía:

miLista = [10, 8, 6, 4, 2]
nuevaLista = miLista [-1:1]
print(nuevaLista)

La salida del fragmento es:  [] .

Rodajas: continuación
Si omites  inicio  en tu rodaja, se supone que deseas obtener un segmento que
comienza en el elemento con índice  0 .

En otras palabras, la rodaja sería de esta forma:

miLista[:fin]

Es un equivalente más compacto:


miLista[0:fin]

Observa el fragmento de código a continuación:

miLista = [10, 8, 6, 4, 2]

nuevaLista = miLista [:3]

print(nuevaLista)

Es por esto que su salida es:  [10, 8, 6] .

Del mismo modo, si omites el  fin  en tu rodaja, se supone que deseas que el
segmento termine en el elemento con el índice  len(miLista) .

En otras palabras, la rodaja sería de esta forma:

miLista[inicio:]

Es un equivalente más compacto:

miLista[inicio:len(miLista)]

Observa el siguiente fragmento de código:

miLista = [10, 8, 6, 4, 2]

nuevaLista = miLista[3:]

print(nuevaLista)

Por lo tanto, la salida es:  [4, 2] .

Rodajas: continuación
Como hemos dicho antes, el omitir  inicio  y  fin  hace una copia de toda la lista:

miLista = [10, 8, 6, 4, 2]

nuevLista = miLista [:]

print(nuevLista)

El resultado del fragmento es:  [10, 8, 6, 4, 2] .


La instrucción  del  descrita anteriormente puede eliminar más de un elemento de la
lista a la vez, también puede eliminar rodajas:

miLista = [10, 8, 6, 4, 2]

del miLista[1:3]

print(miLista)

Nota: En este caso, la rodaja ¡no produce ninguna lista nueva!

La salida del fragmento es: [10, 4, 2] .

También es posible eliminar todos los elementos a la vez:

miLista = [10, 8, 6, 4, 2]

del miLista[:]

print(miLista)

La lista se queda vacía y la salida es:  [] .

Al eliminar la rodaja del código, su significado cambia dramáticamente.

Echa un vistazo:

miLista = [10, 8, 6, 4, 2]

del miLista

print(miLista)

La instrucción  del  eliminará la lista, no su contenido.

La función  print()  de la última línea del código provocará un error de ejecución.

Los operadores in y not
Python ofrece dos operadores muy poderosos, capaces de revisar la lista para
verificar si un valor específico está almacenado dentro de la lista o no.

Estos operadores son:

elem in miLista

elem not in miLista

El primero de ellos ( in ) verifica si un elemento dado(su argumento izquierdo) está


actualmente almacenado en algún lugar dentro de la lista(el argumento derecho) - el
operador devuelve  True  en este caso.

El segundo ( not in ) comprueba si un elemento dado (su argumento izquierdo) está


ausente en una lista - el operador devuelve  True  en este caso.

Observa el código en el editor. El fragmento muestra ambos operadores en acción.


¿Puedes adivinar su salida? Ejecuta el programa para comprobar si tenías razón.

miLista = [0, 3, 12, 8, 2]

print(5 in miLista)
print(5 not in miLista)
print(12 in miLista)

False
True
True

Listas - algunos programas simples


Ahora queremos mostrarte algunos programas simples que utilizan listas.

El primero de ellos intenta encontrar el mayor valor en la lista. Mira el código en el


editor.

El concepto es bastante simple: asumimos temporalmente que el primer elemento es


el más grande y comparamos la hipótesis con todos los elementos restantes de la lista.

El código da como resultado el 17 (como se espera).


El código puede ser reescrito para hacer uso de la forma recién introducida del
ciclo  for :

miLista = [17, 3, 11, 5, 1, 9, 7, 15, 13]

mayor = miLista [0]

for i in miLista:

if i > mayor:

mayor = i

print(mayor)

El programa anterior realiza una comparación innecesaria, cuando el primer elemento


se compara consigo mismo, pero esto no es un problema en absoluto.

El código da como resultado el  17  también (nada inusual).

Si necesitas ahorrar energía de la computadora, puedes usar una rodaja:

miLista = [17, 3, 11, 5, 1, 9, 7, 15, 13]

mayor = miLista [0]

for i in miLista [1:]:

if i > mayor:

mayor = i

print(mayor)

La pregunta es: ¿Cuál de estas dos acciones consume más recursos informáticos: solo
una comparación o partir casi todos los elementos de una lista?

Listas - algunos programas simples


Ahora encontremos la ubicación de un elemento dado dentro de una lista:

miLista = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


Encontrar = 5
Encontrado = False

for i in range(len(miLista)):
Encontrado = miLista[i] == Encontrar
if Encontrado:
break

if Encontrado:
print("Elemento encontrado en el índice", i)
else:
print("ausente")

Elemento encontrado en el índice 4

Nota:

 El valor buscado se almacena en la variable  Encontrar .


 El estado actual de la búsqueda se almacena en la
variable  Encontrado  ( True / False ).
 Cuando  Encontrado  se convierte en  True , se sale del bucle  for .

Supongamos que has elegido los siguientes números en la lotería:  3 ,  7 ,  11 ,  42 ,  34 ,  49 .

Los números que han salido sorteados son:  5 ,  11 ,  9 ,  42 ,  3 ,  49 .

La pregunta es: ¿A cuántos números le has atinado?

El programa te dará la respuesta:

sorteados = [5, 11, 9, 42, 3, 49]


seleccionados = [3, 7, 11, 42, 34, 49]
aciertos = 0

for numeros in seleccionados:


if numeros in sorteados:
aciertos += 1

print(aciertos)

Nota:

 La lista  sorteados  almacena todos los números ganadores.


 La lista de  seleccionados  almacena con números con que se juega.
 La variable  aciertos  cuenta tus aciertos.
La salida del programa es:  4 .

LABORATORIO

Tiempo estimado
10-15 minutos

Nivel de dificultad
Fácil

Objetivos
Familiarizar al estudiante con:

 Indexación de listas.
 Utilizar operadores in  y  not in .

Escenario
Imagina una lista: no muy larga ni muy complicada, solo una lista simple que contiene
algunos números enteros. Algunos de estos números pueden estar repetidos, y esta
es la clave. No queremos ninguna repetición. Queremos que sean eliminados.

Tu tarea es escribir un programa que elimine todas las repeticiones de números de la


lista. El objetivo es tener una lista en la que todos los números aparezcan no más de
una vez.

Nota: Asume que la lista original está ya dentro del código, no tienes que ingresarla
desde el teclado. Por supuesto, puedes mejorar el código y agregar una parte que
pueda llevar a cabo una conversación con el usuario y obtener todos los datos.

Sugerencia: Te recomendamos que crees una nueva lista como área de trabajo
temporal, no necesitas actualizar la lista actual.

No hemos proporcionado datos de prueba, ya que sería demasiado fácil. Puedes usar
nuestro esqueleto en su lugar.

miLista = [1, 2, 4, 4, 1, 4, 2, 6, 2, 9]

#
# coloca tu código aquí

nuevalista = miLista[2:9]

for i in miLista:

if i in nuevalista:

del miLista[i]

print("La lista solo con elementos únicos:")

print(miLista)

La lista solo con elementos únicos:


[1, 4, 6, 2, 9]

RESUMEN DE LA SECCIÓN

Puntos clave

1. Si tienes una lista  l1 , la siguiente asignación:  l2 = l1  no hace una copia de la lista  l1 , pero
hace que las variables  l1  y  l2  apunten a la misma lista en la memoria . Por ejemplo:

vehiculosUno = ['carro', 'bicicleta', 'moto']

print(vehiculosUno) # salida: ['carro', 'bicicleta', 'moto']

vehiculosDos = vehiculosUno

del vehiculosUno[0] # borra 'carro'

print(vehiculosDos) # salida: ['bicicleta', 'moto']

2. Si deseas copiar una lista o parte de la lista, puede hacerlo haciendo uso de rodajas(slicing):

colores = ['rojo', 'verde', 'naranja']


copiaTodosColores = colores[:] # copia la lista completa

copiaParteColores = colores[0:2] # copia parte de la lista

3. También puede utilizar índices negativos para hacer uso de rodajas. Por ejemplo:

listaMuestra = ["A", "B", "C", "D", "E"]

nuevaLista = listaMuestra[2:-1]

print(nuevaLista) # salida: ['C', 'D']

4. Los parámetros  inicio  y  fin son opcionales al partir en rodajas una


lista:  lista[inicio:fin] , por ejemplo:

miLista = [1, 2, 3, 4, 5]

rodajaUno = miLista [2:]

rodajaDos = miLista [:2]

rodajaTres = miLista [-2:]

print(rodajaUno) # salidas: [3, 4, 5]

print(rodajaDos) # salidas: [1, 2]

print(rodajaTres) # salidas: [4, 5]

5. Puedes eliminar rodajas utilizando la instrucción  del :

miLista = [1, 2, 3, 4, 5]

del miLista [0:2]

print(miLista) # salida: [3, 4, 5]

del miLista[:]

print(miLista) # elimina el contenido de la lista, genera: []

6. Puedes probar si algunos elementos existen en una lista o no utilizando las palabras


clave  in  y  not in , por ejemplo:

miLista = ["A", "B", 1, 2]


print("A" in miLista) # salida: True

print("C" not in miLista) # salida: False

print(2 not in miLista) # salidas: False

Ejercicio 1

¿Cuál es la salida del siguiente fragmento de código?

l1 = ["A", "B", "C"]

l2 = l1

l3 = l2

del l1[0]

del l2[0]

print(l3)
Revisar

['C']

Ejercicio 2

¿Cuál es la salida del siguiente fragmento de código?

l1 = ["A", "B", "C"]

l2 = l1

l3 = l2

del l1[0]

del l2
print(l3)
Revisar

['B', 'C']

Ejercicio 3

¿Cuál es la salida del siguiente fragmento de código?

l1 = ["A", "B", "C"]

l2 = l1

l3 = l2

del l1[0]

del l2[:]

print(l3)
Revisar

[]

Ejercicio 4

¿Cuál es la salida del siguiente fragmento de código?

l1 = ["A", "B", "C"]

l2 = l1[:]

l3 = l2[:]

del l1[0]

del l2[0]

print(l3)
Revisar
['A', 'B', 'C']

Ejercicio 5

Inserte  in  o  not in  en lugar de  ???  para que el código genere el resultado esperado.

miLista = [1, 2, "in", True, "ABC"]

print(1 ??? miLista) # salida True

print("A" ??? miLista) # salida True

print(3 ??? miLista) # salida True

print(False ??? miLista) # salida False


Revisar

miLista = [1, 2, "in", True, "ABC"]

print(1 in miLista) # salidas True

print("A" not in miLista) # salida True

print(3 not in miLista) # salida True

print(False in miLista) # salida False

 3.1.7.1 Listas en aplicaciones avanzadas

Listas dentro de listas


Las listas pueden constar de escalares (es decir, números) y elementos de una
estructura mucho más compleja (ya has visto ejemplos como cadenas, booleanos o
incluso otras listas en las lecciones del Resumen de la Sección anterior). Veamos más
de cerca el caso en el que los elementos de una lista son solo listas.

A menudo encontramos estos arreglos en nuestras vidas. Probablemente el mejor


ejemplo de esto sea un tablero de ajedrez.
Un tablero de ajedrez está compuesto de filas y columnas. Hay ocho filas y ocho
columnas. Cada columna está marcada con las letras de la A a la H. Cada línea está
marcada con un número del uno al ocho.

La ubicación de cada campo se identifica por pares de letras y dígitos. Por lo tanto,
sabemos que la esquina inferior derecha del tablero (la que tiene la torre blanca) es
A1, mientras que la esquina opuesta es H8.

Supongamos que podemos usar los números seleccionados para representar


cualquier pieza de ajedrez. También podemos asumir que cada fila en el tablero de
ajedrez es una lista.

Observa el siguiente código:

fila = []

for i in range(8):
row.append(PEON_BLANCO)

Crea una lista que contiene ocho elementos que representan la segunda fila del
tablero de ajedrez: la que está llena de peones (supon que  PEON_BLANCO  es
un símbolo predefinido que representa un peón blanco).

El mismo efecto se puede lograr mediante una comprensión de lista, la sintaxis


especial utilizada por Python para completar o llenar listas masivas.

Una comprensión de lista es en realidad una lista, pero se creó sobre la marcha
durante la ejecución del programa, y no se describe de forma estática.

Echa un vistazo al fragmento:

fila = [PEON_BLANCO for i in range(8)]

La parte del código colocada dentro de los paréntesis especifica:

 Los datos que se utilizarán para completar la lista ( PEON_BLANCO )


 La cláusula que especifica cuántas veces se producen los datos dentro de la
lista ( for i in range(8) )

Permítenos mostrarte otros ejemplos de comprensión de lista:


Ejemplo # 1:

cuadrados = [x ** 2 for x in range(10)]

El fragmento de código genera una lista de diez elementos y rellena con cuadrados de
diez números enteros que comienzan desde cero (0, 1, 4, 9, 16, 25, 36, 49, 64, 81)

Ejemplo # 2:

dos = [2 ** i for i in range(8)]

El fragmento crea un arreglo de ocho elementos que contiene las primeras ocho
potencias del numero dos (1, 2, 4, 8, 16, 32, 64, 128)

Ejemplo # 3:

probabilidades = [x for x in cuadrados if x % 2 != 0]

El fragmento hace una lista con solo los elementos impares de la lista  cuadrados .

Listas dentro de listas: arreglos bidimensionales


Supongamos también que un símbolo predefinido denominado  EMPTY  designa un
campo vacío en el tablero de ajedrez.

Entonces, si queremos crear una lista de listas que representan todo el tablero de
ajedrez, se puede hacer de la siguiente manera:

tablero = []

for i in range(8):
fila = [EMPTY for i in range(8)]
tablero.append(fila)

Nota:

 La parte interior del bucle crea una fila que consta de ocho elementos(cada
uno de ellos es igual a  EMPTY ) y lo agrega a la lista del  tablero .
 La parte exterior se repite ocho veces.
 En total, la lista  tablero  consta de 64 elementos (todos iguales a  EMPTY ).

Este modelo imita perfectamente el tablero de ajedrez real, que en realidad es una
lista de elementos de ocho elementos, todos ellos en filas individuales. Resumamos
nuestras observaciones:
 Los elementos de las filas son campos, ocho de ellos por fila.
 Los elementos del tablero de ajedrez son filas, ocho de ellos por tablero de
ajedrez.

La variable  tablero  ahora es un arreglo bidimensional. También se le llama, por


analogía a los términos algebraicos, una matriz.

Como las listas de comprensión puede ser anidadas, podemos acortar la creación del
tablero de la siguiente manera:

tablero = [[EMPTY for i in range(8)] for j in range(8)]

La parte interna crea una fila, y la parte externa crea una lista de filas.

Listas dentro de listas: arreglos bidimensionales -


continuación
El acceso al campo seleccionado del tablero requiere dos índices: el primero selecciona
la fila; el segundo: el número del campo dentro de la fila, el cual es un número de
columna.

Echa un vistazo al tablero de ajedrez. Cada campo contiene un par de índices que se
deben dar para acceder al contenido del campo:
Echando un vistazo a la figura que se muestra arriba, coloquemos algunas piezas de
ajedrez en el tablero. Primero, agreguemos todas las torres:

tablero[0][0] = TORRE

tablero[0][7] = TORRE

tablero[7][0] = TORRE

tablero[7][7] = TORRE

Si deseas agregar un caballo a C4, hazlo de la siguiente manera:

tablero[4][2] = CABALLO

Y ahora un peón a E5:


tablero[3][4] = PEON

Y ahora - experimenta con el código en el editor.

EMPTY = "-"

TORRE = "TORRE"

tablero = []

for i in range(8):

fila = [EMPTY for i in range(8)]

tablero.append (fila)

tablero[0][0] = TORRE

tablero[0][7] = TORRE

tablero[7][0] = TORRE

tablero[7][7] = TORRE

print(tablero)

Naturaleza multidimensional de las listas:


aplicaciones avanzadas
Profundicemos en la naturaleza multidimensional de las listas. Para encontrar
cualquier elemento de una lista bidimensional, debes usar dos coordenadas:

 Una vertical (número de fila).


 Una horizontal (número de columna).

Imagina que desarrollas una pieza de software para una estación meteorológica
automática. El dispositivo registra la temperatura del aire cada hora y lo hace durante
todo el mes. Esto te da un total de 24 × 31 = 744 valores. Intentemos diseñar una lista
capaz de almacenar todos estos resultados.
Primero, debes decidir qué tipo de datos sería adecuado para esta aplicación. En este
caso, sería mejor un  float , ya que este termómetro puede medir la temperatura con
una precisión de 0.1 ℃.

Luego tomarás la decisión arbitraria de que las filas registrarán las lecturas cada hora
exactamente (por lo que la fila tendrá 24 elementos) y cada una de las filas se asignará
a un día del mes (supongamos que cada mes tiene 31 días, por lo que necesita 31
filas). Aquí está el par apropiado de comprensiones( h  es para las horas,  d para el día):

temps = [[0.0 for h in range (24)] for d in range (31)]

Toda la matriz está llena de ceros ahora. Puede suponer que se actualiza
automáticamente utilizando agentes de hardware especiales. Lo que tienes que hacer
es esperar a que la matriz se llene con las mediciones.

Ahora es el momento de determinar la temperatura promedio mensual del mediodía.


Suma las 31 lecturas registradas al mediodía y divida la suma por 31. Puedes suponer
que la temperatura de medianoche se almacena primero. Aquí está el código:

temps = [[0.0 for h in range(24)] for d in range (31)]


#
# la matriz se actualiza mágicamente aquí
#

suma = 0.0

for day in temps:


suma += day[11]

promedio= suma / 31

print("Temperatura promedio al mediodía:", promedio)

Nota: La variable  day  utilizada por el bucle  for  no es un escalar: cada paso a través de
la matriz  temps  lo asigna a la siguiente fila de la matriz; Por lo tanto, es una lista. Se
debe indexar con  11  para acceder al valor de temperatura medida al mediodía.

Ahora encuentra la temperatura más alta durante todo el mes, ve el código:

temps = [[0.0 for h in range (24)] for d in range (31)]


#
# la matriz se actualiza mágicamente aquí
#
mas_alta = -100.0

for day in temps:


for temp in day:
if temp > mas_alta:
mas_alta = temp

print("La temperatura más alta fue:", mas_alta)

Nota:

 La variable  day  itera en todas las filas de la matriz  temps .


 La variable  temp  itera a través de todas las mediciones tomadas en un día.

Ahora cuenta los días en que la temperatura al mediodía fue de al menos 20 ℃:

temps = [[0.0 for h in range(24)] for d in range(31)]


#
# la matriz se actualiza mágicamente aquí
#

hotDays = 0

for day in temps:


if day[11] > 20.0:
hotDays += 1

print(hotDays, " fueron los días calurosos.")

Arreglos tridimensionales
Python no limita la profundidad de la inclusión lista en lista. Aquí puedes ver un
ejemplo de un arreglo tridimensional:

Imagina un hotel. Es un hotel enorme que consta de tres edificios, de 15 pisos cada
uno. Hay 20 habitaciones en cada piso. Para esto, necesitas un arreglo que pueda
recopilar y procesar información sobre las habitaciones ocupadas/libres.

Primer paso: El tipo de elementos del arreglo. En este caso, sería un valor booleano
( True / False ).

Paso dos: Análisis de la situación. Resume la información disponible: tres edificios, 15


pisos, 20 habitaciones.
Ahora puedes crear el arreglo:

habitaciones = [[[False for r in range(20)] for f in range(15)] for


t in range(3)]

El primer índice ( 0  a  2 ) selecciona uno de los edificios; el segundo( 0  a  14 ) selecciona el
piso, el tercero ( 0  a  19 ) selecciona el número de habitación. Todas las habitaciones
están inicialmente desocupadas.

Ahora ya puedes reservar una habitación para dos recién casados: en el segundo
edificio, en el décimo piso, habitación 14:

habitaciones[1][9][13] = True

y desocupa el segundo cuarto en el quinto piso ubicado en el primer edificio:

habitaciones[0][4][1] = False

Verifica si hay disponibilidad en el piso 15 del tercer edificio:

vacante = 0

for numeroHabitacion in range(20):

if not habitaciones[2][14][numeroHabitacion]:

vacante += 1

La variable  vacante  contiene  0  si todas las habitaciones están ocupadas, o en dado
caso el número de habitaciones disponibles.

¡Felicitaciones! Has llegado al final del módulo. ¡Sigue con el buen trabajo!

RESUMEN DE LA SECCIÓN

Puntos clave
1. La comprensión de listas te permite crear nuevas listas a partir de las existentes de una manera
concisa y elegante. La sintaxis de una lista de comprensión es la siguiente:

[expresión for elemento in lista if condicional]

El cual es un equivalente del siguiente código:

for elemento in lista:

if condicional:

expresión

Este es un ejemplo de una lista de comprensión: el código siguiente crea una lista de cinco elementos
con los primeros cinco números naturales elevados a la potencia de 3:

cubos = [num ** 3 for num in range (5)]

print(cubos) # salidas: [0, 1, 8, 27, 64]

2. Puedes usar listas anidadas en Python para crear matrices (es decir, listas bidimensionales). Por
ejemplo:

# Una tabla de cuatro columnas y cuatro filas: un arreglo


bidimensional (4x4)
table = [[":(", ":)", ":(", ":)"],

[":)", ":(", ":)", ":)"],

[":(", ":)", ":)", ":("],

[":)", ":)", ":)", ":("]]

print(tabla)

print(tabla [0][0]) # salida: ':('

print(tabla [0][3]) # salida: ':)'

3. Puedes anidar tantas listas en las listas como desee y, por lo tanto, crear listas n-dimensionales,
por ejemplo, arreglos de tres, cuatro o incluso sesenta y cuatro dimensiones. Por ejemplo:

# Cubo - un arreglo tridimensional (3x3x3)

cubo = [[[':(', 'x', 'x'],

[':)', 'x', 'x'],

[':(', 'x', 'x']],


[[':)', 'x', 'x'],

[':(', 'x', 'x'],

[':)', 'x', 'x']],

[[':(', 'x', 'x'],

[':)', 'x', 'x'],

[':)', 'x', 'x']]]

print(cubo)

print(cubo [0][0][0]) # salida: ':('

print(cubo [2][2][0]) # salida: ':)'

 4.1.1.2 Funciones

¿Por qué necesitamos funciones?


Hasta ahorita has implementado varias veces el uso de funciones, pero solo se han visto algunas de
sus ventajas. Solo se han invocado funciones para utilizarlas como herramientas, con el fin de hacer
la vida mas fácil, y para simplificar tareas tediosas y repetitivas.

Cuando se desea mostrar o imprimir algo en consola se utiliza  print() . Cuando se desea leer el
valor de una variable se emplea  input() , combinados posiblemente con  int()  o  float() .

También se ha hecho uso de algunos métodos, las cuales también son funciones, pero declarados de
una manera muy especifica.

Ahora aprenderás a escribir tus propias funciones, y como utilizarlas. Escribiremos varias de ellas
juntos, desde muy sencillas hasta algo complejas. Se requerirá de tu concentración y atención.

Muy a menudo ocurre que un cierto fragmento de código se repite muchas veces en un programa.
Se repite de manera literal o, con algunas modificaciones menores, empleando algunas otras
variables dentro del programa. También ocurre que un programador ha comenzado a copiar y pegar
ciertas partes del código en más de una ocasión en el mismo programa.

Puede ser muy frustrante percatarse de repente que existe un error en el código copiado. El
programador tendrá que escarbar bastante para encontrar todos los lugares en el código donde hay
que corregir el error. Además, existe un gran riesgo de que las correcciones produzcan errores
adicionales.
Definamos la primer condición por la cual es una buena idea comenzar a escribir funciones
propias: si un fragmento de código comienza a aparecer en más de una ocasión, considera la
posibilidad de aislarlo en la forma de una función invocando la función desde el lugar en el que
originalmente se encontraba.

Puede suceder que el algoritmo que se desea implementar sea tan complejo que el código comience a
crecer de manera incontrolada y, de repente, ya no se puede navegar por él tan fácilmente.

Se puede intentar solucionar este problema comentando el código, pero pronto te darás cuenta que
esto empeorará la situación - demasiados comentarios hacen que el código sea más difícil de leer
y entender. Algunos dicen que una función bien escrita debe ser comprensible con tan solo una
mirada.

Un buen desarrollador divide el código (o mejor dicho: el problema) en piezas aisladas, y codifica


cada una de ellas en la forma de una función.

Esto simplifica considerablemente el trabajo del programa, debido a que cada pieza se codifica por
separado y consecuentemente se prueba por separado. A este proceso se le llama
comúnmente descomposición.
Existe una segunda condición: si un fragmento de código se hace tan extenso que leerlo o
entenderlo se hace complicado, considera dividirlo pequeños problemas por separado e
implementa cada uno de ellos como una función independiente.

Esta descomposición continua hasta que se obtiene un conjunto de funciones cortas, fáciles de
comprender y probar.

Descomposición
Es muy común que un programa sea tan largo y complejo que no puede ser asignado a un solo
desarrollador, y en su lugar un equipo de desarrolladores trabajarán en el. El problema, debe ser
dividido entre varios desarrolladores de una manera en que se pueda asegurar su eficiencia y
cooperación.
Es inconcebible que más de un programador deba escribir el mismo código al mismo tiempo, por lo
tanto, el trabajo debe de ser dividido entre todos los miembros del equipo.

Este tipo de descomposición tiene diferentes propósitos, no solo se trata de compartir el trabajo,
sino también de compartir la responsabilidad entre varios desarrolladores.

Cada uno debe escribir un conjunto bien definido y claro de funciones, las cuales al ser combinadas
dentro de un módulo (esto se clarificara un poco mas adelante) nos dará como resultado el producto
final.

Esto nos lleva directamente a la tercera condición: si se va a dividir el trabajo entre varios
programadores, se debe descomponer el problema para permitir que el producto sea
implementado como un conjunto de funciones escritas por separado empacadas juntas en
diferentes módulos.

¿De dónde provienen las funciones?


En general, las funciones provienen de al menos tres lugares:

 De Python mismo: varias funciones (como  print() ) son una parte integral de Python, y
siempre están disponibles sin algún esfuerzo adicional del programador; se les llama a estas
funciones funciones integradas.
 De los módulos preinstalados de Python: muchas de las funciones, las cuales comúnmente
son menos utilizadas que las integradas, están disponibles en módulos instalados juntamente
con Python; para poder utilizar estas funciones el programador debe realizar algunos pasos
adicionales (se explicará acerca de esto en un momento).
 Directamente del código: tu puedes escribir tus propias funciones, colocarlas dentro del
código, y usarlas libremente.
 Existe una posibilidad más, pero se relaciona con clases, se omitirá por ahora.

Tu primer función
Observa el fragmento de código en el editor.

Es bastante sencillo, es un ejemplo de como transformar una parte de código que


se esta repitiendo en una función.

El mensaje enviado a la consola por la función  print()  es siempre el mismo. El código
es funcional y no contiene errores, sin embargo imagina tendrías que hacer si tu jefe
pidiera cambiar el mensaje para que fuese mas cortés, por ejemplo, que comience con
la frase  "Por favor," .

Tendrías que tomar algo de tiempo para cambiar el mensaje en todos los lugares
donde aparece (podrías hacer uso de copiar y pegar, pero eso no lo haría mas
sencillo). Es muy probable que cometas errores durante el proceso de corrección, eso
traería frustración a ti y a tu jefe.

¿Es posible separar ese código repetido, darle un nombre y hacerlo reutilizable?


Significaría que el cambio hecho en un solo lugar será propagado a todos los
lugares donde se utilice.

Para que esto funcione, dicho código debe ser invocado cada vez que se requiera.

Es posible, esto es exactamente para lo que existen las funciones.

print("Ingresa un valor: ")


a = int(input())

print("Ingresa un valor: ")


b = int(input())

print("Ingresa un valor: ")


c = int(input())

Tu primer función
¿Cómo es que se crea dicha función?

Se necesita definirla. Aquí, la palabra define es significativa.

Así es como se ve la definición más simple de una función:

def nombreFuncion():
cuerpoFuncion

 Siempre comienza con la palabra reservada  def  (que significa definir)


 Después de  def  va el nombre de la función (las reglas para darle nombre a
las funciones son las mismas que para las variables).
 Después del nombre de la función, hay un espacio para un par
de paréntesis (ahorita no contienen algo, pero eso cambiará pronto).
 La línea debe de terminar con dos puntos.
 La línea inmediatamente después de  def  marca el comienzo del cuerpo de la
función - donde varias o (al menos una). instrucción anidada, será ejecutada
cada vez que la función sea invocada; nota: la función termina donde el
anidamiento termina, se debe ser cauteloso.
A continuación se definirá la función. Se llamará  mensaje  - aquí esta:

def mensaje():
print("Ingresa un valor: ")

La función es muy sencilla, pero completamente utilizable. Se ha nombrado  mensaje ,


pero eso es opcional, tu puedes cambiarlo. Hagamos uso de ella.

El código ahora contiene la definición de la función:

def mensaje():
print("Ingresa un valor: ")

print("Se comienza aquí.")


print("Se termina aquí.")

Nota: no se esta utilizando la función, no se esta invocando en el código.

Al correr el programa, se mostrará lo siguiente:

Se comienza aquí.
Se termina aquí.

Esto significa que Python lee la definición de la función y la recuerda, pero no la


ejecuta sin permiso.

Se ha modificado el código, se ha insertado la invocación de la función entre los dos


mensajes:

def mensaje():
print("Ingresa un valor: ")

print("Se comienza aquí.")


mensaje()
print("Se termina aquí.")

La salida ahora se ve diferente:


Se comienza aquí.
Ingresa un valor:
Se termina aquí.

Prueba el código, modifícalo, experimenta con el.

El funcionamiento de las funciones


Observa la imagen:

La imagen intenta mostrar el proceso completo:

 Cuando se invoca una función, Python recuerda el lugar donde esto ocurre y salta hacia
dentro de la función invocada.
 El cuerpo de la función es entonces ejecutado.
 Al llegar al final de la función, Python regresa al lugar inmediato después de donde ocurrió
la invocación.

Existen dos consideraciones muy importantes, la primera de ella es:

No se debe invocar una función antes de que se haya definido.

Recuerda: Python lee el código de arriba hacia abajo. No va a adelantarse en el código para
determinar si la función invocada esta definida mas adelante, el lugar correcto para definirla es antes
de ser invocada.

Se ha insertado un error en el código anterior - ¿Puedes notar la diferencia?


print("Se comienza aquí.")
mensaje()
print("Se termina aquí.")

def mensaje():
print("Ingresa un valor: ")

Se ha movido la función al final del código. ¿Podrá Python encontrarla cuando la ejecución llegue a
la invocación?

No, no podrá. El mensaje de error dirá:

NameError: name 'mensaje' is not defined

No intentes forzar a Python a encontrar funciones que no están definidas en el lugar correcto.

La segunda consideración es mas sencilla:

Una función y una variable no pueden compartir el mismo nombre.

El siguiente fragmento de código es erróneo:

def mensaje():
print("Ingresa un valor: ")

mensaje = 1

El asignar un valor al nombre "mensaje" causa que Python olvide su rol anterior. La función con el
nombre de  mensaje  ya no estará disponible.

Afortunadamente, es posible combinar o mezclar el código con las funciones - no es forzoso


colocar todas las funciones al inicio del archivo fuente.

Observa el siguiente código:

print("Se comienza aquí.")

def mensaje():
print("Ingresa un valor: ")

mensaje()

print("Se termina aquí.")

Puede verse extraño, pero es completamente correcto, y funciona como se necesita.


Regresemos al ejemplo inicial para implementar la función de manera correcta:

def mensaje():
print("Ingresa un valor: ")

mensaje()
a = int(input())
mensaje()
b = int(input())
mensaje()
c = int(input())

El modificar el mensaje de entrada es ahora sencillo: se puede hacer con solo modificar el código
una única vez - dentro del cuerpo de la función.

Abre Sandbox, e inténtalo tu mismo.

 RESUMEN DE SECCIÓN

Puntos Clave

1. Una función es un bloque de código que realiza una tarea especifica cuando la función es llamada
(invocada). Las funciones son útiles para hacer que el código sea reutilizable, que este mejor
organizado y más legible. Las funciones contienen parámetros y pueden regresar valores.

2. Existen al menos cuatro tipos de funciones básicas en Python:

 Funciones integradas las cuales son partes importantes de Python (como lo es la


función  print() ). Puedes ver una lista completa de las funciones integradas de Python en
la siguiente liga: https://docs.python.org/3/library/functions.html.
 También están las que se encuentran en módulos pre-instalados (se hablará acerca de ellas
en el Módulo 5 de este curso).
 Funciones definidas por el usuario las cuales son escritas por los programadores para los
programadores, puedes escribir tus propias funciones y utilizarlas libremente en tu código.
 Las funciones  lambda  (aprenderás acerca de ellas en el Módulo 6 del curso).

3. Las funciones propias se pueden definir utilizando la palabra reservada  def  y con la siguiente
sintaxis:

def tuFuncion (parámetros opcionales):


# el cuerpo de la función

Se puede definir una función sin que haga uso de argumentos, por ejemplo:

def mensaje(): # definiendo una función

print("Hola") # cuerpo de la función

mensaje() # invocación de la función

También es posible definir funciones con argumentos, como la siguiente que contiene un solo
parámetro:

def hola(nombre): # definiendo una función

print("Hola,", nombre) # cuerpo de la función

nombre = input("Ingresa tu nombre: ")

hola(nombre) # invocación de la función

Se hablará mas acerca de funciones con parámetros en la siguiente sección.

Ejercicio 1

La función  input()  es un ejemplo de:

a) una función definida por el usuario


b) una función integrada

Revisar

b - es una función integrada


Ejercicio 2

¿Qué es lo que ocurre cuando se invoca una función antes de ser definida? Ejemplo:

hola()

def hola():

print("hola!")
Revisar

Se genera una excepción (la excepción  NameError )

Ejercicio 3

¿Qué es lo que ocurrirá cuando se ejecute el siguiente código?

def hola():

print("hola")

hola(5)
Revisar

Se genera una excepción (la excepción  TypeError ) - la función  hola()  no toma argumentos.

 4.1.2.1 Como las funciones se comunican con su entorno

Funciones con parámetros


El potencial completo de una función se revela cuando puede ser equipada con una interface que es
capaz de aceptar datos provenientes de la invocación. Dichos datos pueden modificar el
comportamiento de la función, haciéndola mas flexible y adaptable a condiciones cambiantes.

Un parámetro es una variable, pero existen dos factores que hacen a un parámetro diferente:

 Los parámetros solo existen dentro de las funciones en donde han sido definidos, y el
único lugar donde un parámetro puede ser definido es entre los paréntesis después del
nombre de la función, donde se encuentra la palabra reservada  def .
 La asignación de un valor a un parámetro de una función se hace en el momento en
que la función se manda llamar o se invoca, especificando el argumento correspondiente.
def funcion(parametro):
###

Recuerda que:

 Los parámetros solo existen dentro de las funciones (este es su entorno natural).


 Los argumentos existen fuera de las funciones, y son los que pasan los valores a los
parámetros correspondientes.

Existe una clara división entre estos dos mundos.

Enriquezcamos la función anterior agregándole un parámetro, se utilizará para mostrar al usuario el


valor de un número que la función pide.

Se tendrá que modificar la definición  def  de la función, así es como se ve ahora:

def mensaje(numero):
###

Esta definición especifica que nuestra función opera con un solo parámetro con el nombre
de  numero . Se puede utilizar como una variable normal, pero solo dentro de la función - no es
visible en otro lugar.

Ahora hay que mejorar el cuerpo de la función:

def mensaje(numero):
print("Ingresa el número:", numero)

Se ha hecho buen uso del parámetro. Nota: No se le ha asignado al parámetro algún valor. ¿Es
correcto?

Si, lo es.

Un valor para el parámetro llegará del entorno de la función.

Recuerda: especificar uno o mas parámetros en la definición de la función es un requerimiento, y


se debe de cumplir durante la invocación de la misma. Se debe proveer el mismo numero de
argumentos como haya parámetros definidos.

El no hacerlo provocará un error.

Intenta ejecutar el código en el editor.


Esto es lo que aparecerá en consola:

TypeError: mensaje() missing 1 required positional argument:


'numero'

Esto significa que se esta invocando la función pero esta faltando el argumento.

Aquí esta ya de manera correcta:

def mensaje(numero):
print("Ingresa un número:", numero)

mensaje(1)

De esta manera ya esta correcto. El código producirá la siguiente salida:

Ingresa un número: 1

¿Puedes ver como funciona? El valor del argumento utilizado durante la invocación ( 1 )
ha sido pasado a la función, dándole un valor inicial al parámetro con el nombre
de  numero .

Existe una circunstancia importante que se debe mencionar.

Es posible tener una variable con el mismo nombre del parámetro de la función.

El siguiente código muestra un ejemplo de esto:

def mensaje(numero):
print("Ingresa un número:", numero)

numero = 1234
mensaje(1)
print(numero)

Una situación como la anterior, activa un mecanismo denominado sombreado:

 El parámetro  x  sombrea cualquier variable con el mismo nombre, pero...


 ... solo dentro de la función que define el parámetro.

El parámetro llamado  numero  es una entidad completamente diferente de la variable


llamada  numero .
Esto significa que el código anterior producirá la siguiente salida:

Ingresa un número: 1
1234

Una función puede tener tantos parámetros como se desee, pero entre más
parámetros, es más difícil memorizar su rol y propósito.

Modifiquemos la función- ahora tiene dos parámetros:

def mensaje(que, numero):


print("Ingresa", que, "número", numero)

Esto significa que para invocar la función, se necesitan dos argumentos.

El primer valor va a contener el nombre del valor deseado.

Aquí esta:
def mensaje(que, numero):
print("Ingresa", que, "número", numero)

mensaje("teléfono", 11)
mensaje("precio", 5)
mensaje("número", "número")

Estas es la salida del código anterior:

Ingresa teléfono número 11


Ingresa precio número 5
Ingresa número número número

Ejecuta el código, modifícalo, agrega mas parámetros y ve como esto afecta la salida.

Paso de parámetros posicionales


La técnica que asigna cada argumento al parámetro correspondiente, es llamada paso
de parámetros posicionales, los argumentos pasados de esta manera son
llamados argumentos posicionales.

Ya se ha utilizado, pero Python ofrece mucho más. Se abordará este tema a


continuación.

def miFuncion(a, b, c):

print(a, b, c)

miFuncion(1, 2, 3)

Nota: el paso de parámetros posicionales es usado de manera intuitiva por las


personas en muchas situaciones. Por ejemplo, es generalmente aceptado que cuando
nos presentamos mencionamos primero nuestro nombre(s) y después nuestro
apellido, por ejemplo, "Me llamo Juan Pérez."

Sin embargo, En Hungría se hace al revés.

Implementemos esa costumbre en Python. La siguiente función es utilizada para


presentar a alguien:

def presentar(primerNombre, segundoNombre):

print("Hola, mi nombre es", primerNombre, segundoNombre)


presentar("Luke", "Skywalker")

presentar("Jesse", "Quick")

presentar("Clark", "Kent")

¿Puedes predecir la salida? Ejecuta el código y verifícalo por ti mismo.

Ahora imaginemos que la función esta siendo utilizada en Hungría. En este caso, el
código sería de la siguiente manera:

def presentar(primerNombre, segundoNombre):

print("Hola, mi nombre es", primerNombre, segundoNombre)

presentar("Skywalker" ,"Luke" )

presentar("Quick", "Jesse")

presentar("Kent", "Clark")

La salida será diferente. ¿La puedes predecir?

Ejecuta el código para comprobar tu respuesta. ¿Es lo que esperabas?

¿Puedes construir más funciones de este tipo ?

Paso de argumentos con palabras clave


Python ofrece otra manera de pasar argumentos, donde el significado del
argumento esta definido por su nombre, no su posición, a esto se le denomina paso
de argumentos con palabras clave.

Observa el siguiente código:

def presentar (primerNombre, segundoNombre):

print("Hola, mi nombre es", primerNombre, segundoNombre)

presentar(primerNombre = "James", segundoNombre = "Bond")

presentar(segundoNombre = "Skywalker", primerNombre = "Luke")


El concepto es claro: los valores pasados a los parámetros son precedidos por el
nombre del parámetro al que se le va a pasar el valor, seguido por el signo de  = .

La posición no es relevante aquí, cada argumento conoce su destino con base en el


nombre utilizado.

Debes de poder predecir la salida. Ejecuta el código y verifica tu respuesta.

Por supuesto que no se debe de utilizar el nombre de un parámetro que no existe.

El siguiente código provocará un error de ejecución:

def presentar (primerNombre, segundoNombre):

print("Hola, mi nombre es ", primerNombre, segundoNombre)

introduction(apellido="Skywalker", primerNombre="Luke")

Esto es lo que Python arrojará:

TypeError: presentar() got an unexpected keyword argument


'apellido'

Inténtalo tu mismo.

El combinar argumentos posicionales y de palabras


clave
Es posible combinar ambos tipos si se desea, solo hay una regla inquebrantable: se
deben colocar primero los argumentos posicionales y después los de palabras
clave.

Piénsalo por un momento y entenderás el porque.

Para mostrarte como funciona, se utilizara la siguiente función de tres parámetros:

def suma(a, b, c):


print(a, "+", b, "+", c, "=", a + b + c)

Su propósito es el de evaluar y presentar la suma de todos sus argumentos.

La función, al ser invocada de la siguiente manera:


suma(1, 2, 3)

Dará como salida:

1 + 2 + 3 = 6

Hasta ahorita es un ejemplo puro de un argumento posicional.

También, se puede reemplazar la invocación actual por una con palabras clave, como
la siguiente:

suma(c = 1, a = 2, b = 3)

El programa dará como salida lo siguiente:

2 + 3 + 1 = 6

Ten presente el orden de los valores.

Ahora intentemos mezclar ambas.

Observa la siguiente invocación de la función:

suma(3, c = 1, b = 2)

Vamos a analizarla:

 El argumento ( 3 ) para el parametro  a  es pasado utilizando la manera


posicional.
 Los argumentos para  c  y  b  son especificados con palabras clave.

Esto es lo que se verá en la consola:

3 + 2 + 1 = 6

Se cuidadoso, ten cuidado de no cometer errores. Si se intenta pasar mas de un valor


a un argumento, ocurrirá un error y se mostrará lo siguiente:

Observa la siguiente invocación, se le esta asignando dos veces un valor al


parámetro  a :
suma(3, a = 1, b = 2)

La respuesta de Python es:

TypeError: suma() got multiple values for argument 'a'

Observa el siguiente código. Es un código completamente correcto y funcional, pero


no tiene mucho sentido:

suma(4, 3, c = 2)

Todo es correcto, pero el dejar solo un argumento con palabras clave es algo extraño -
¿Qué es lo que opinas?

Funciones con parámetros: mas detalles


En ocasiones ocurre que algunos valores de ciertos argumentos son mas utilizados
que otros. Dichos argumentos tienen valores predefinidos los cuales pueden ser
considerados cuando los argumentos correspondientes han sido omitidos.

Uno de los apellidos más comunes en Latinoamérica es González. Tomémoslo para el


ejemplo.

El valor por default para el parámetro se asigna de la siguiente manera:

def presentar(primerNombre, segundoNombre="González"):

print("Hola, mi nombre es", primerNombre, segundoNombre)

Solo se tiene que colocar el nombre del parámetro seguido del signo de  =  y el valor
por default.

Invoquemos la función de manera normal:

presentar("Jorge", "Pérez")

¿Puedes predecir la salida del programa? Ejecútalo y revisa si era lo esperado.

¿Y? No parece haber cambiado algo, pero cuando se invoca la función de una manera
inusual, como esta:

presentar("Enrique")
o así:

presentar (primerNombre="Guillermo")

no habrá errores, ambas invocaciones funcionarán, la consola mostrará los siguientes


resultados:

Hola, mi nombre es Enrique González

Hola, mi nombre es Guillermo González

Pruébalo.

Puedes hacerlo con mas parámetros, si te resulta útil. Ambos parámetros tendrán sus
valores por default, observa el siguiente código:

def presentar(primerNombre="Juan", segundoNombre="González"):

print("Hola, mi nombre es ", primerNombre, segundoNombre)

Esto hace que la siguiente invocación sea completamente valida:

presentar ()

Y esta es la salida esperada:

Hola, mi nombre es Juan González

Si solo se especifica un argumento de palabra clave, el restante tomará el valor por


default:

presentar(segundoNombre="Rodríguez")

La salida es:

Hola, mi nombre es Juan Rodríguez

Pruébalo.
Felicidades, has aprendido las maneras básicas de comunicación con funciones.

RESUMEN DE SECCIÓN

Puntos Clave

1. Se puede pasar información a las funciones utilizando parámetros. Las funciones pueden tener
tantos parámetros como sean necesarios.

Un ejemplo de una función con un parámetro:

def hola(nombre):
print("Hola,", nombre)

hola("Greg")

Un ejemplo de una función de dos parámetros:

def holaTodos(nombre1, nombre2):


print("Hola,", nombre2)
print("Hola,", nombre1)

holaTodos("Sebastián", "Felipe")

Un ejemplo de una función de tres parámetros:

def direccion(calle, ciudad, codigoPostal):


print("Tu dirección es:", calle, ciudad, codigoPostal)

c = input("Calle: ")
cp = input("Código Postal: ")
cd = input("Ciudad: ")

address(c, cd, cp)

2. Puedes pasar argumentos a una función utilizando las siguientes técnicas:

 Paso de argumentos posicionales en la cual el orden de los parámetros es relevante


(Ejemplo 1).
 Paso de argumentos con palabras clave en la cual el orden de los argumentos es
irrelevante (Ejemplo 2).
 Una mezcla de argumentos posicionales y con palabras clave (Ejemplo 3).
Ejemplo 1
def resta(a, b):
print(a - b)

resta(5, 2) # salida: 3
resta(2, 5) # salida: -3

Ejemplo 2
def resta(a, b):
print(a - b)

resta(a=5, b=2) # salida: 3


resta(b=2, a=5) # salida: 3

Ex. 3
def resta(a, b):
print(a - b)

resta(5, b=2) # salida: 3


resta(5, 2) # salida: 3

Es importante recordar que primero se especifican los argumentos posicionales y después los de


palabras clave. Es por esa razón que si se intenta ejecutar el siguiente código:

def resta(a, b):


print(a - b)

resta(5, b=2) # salida: 3


resta(a=5, 2) # Syntax Error

Python no lo ejecutará y marcará un error de sintaxis  SyntaxError .

3. Se puede utilizar la técnica de argumentos con palabras clave para asignar valores predefinidos a
los argumentos:

def nombre(nombre, apellido="Pérez"):


print(nombre, apellido)

nombre("Andy") # salida: Andy Pérez


nombre("Bety", "Rodríguez") # salida: Bety Johnson (el argumento
de palabra clave es reemplazado por " Rodríguez ")
Ejercicio 1

¿Cuál es la salida del siguiente código?

def intro(a="James Bond", b="Bond"):


print("Mi nombre es", b + ".", a + ".")

intro()
Revisar

Mi nombre es Bond. James Bond.

Ejercicio 2

¿Cuál es la salida del siguiente código?

def intro(a="James Bond", b="Bond"):


print("Mi nombre es", b + ".", a + ".")

intro(b="Sergio López")
Revisar

Mi nombre es Sergio López. James Bond.

Ejercicio 3

¿Cuál es la salida del siguiente fragmento de código?

def intro(a, b="Bond"):


print("Mi nombre es", b + ".", a + ".")

intro("Susan")
Revisar

Mi nombre es Bond. Susan.

Ejercicio 4

¿Cuál es la salida del siguiente código?


def suma(a, b=2, c):
print(a + b + c)

suma(a=1, c=3)
Revisar

SyntaxError  - a non-default argument ( c ) follows a default argument ( b=2 )

 4.1.3.1 Regresando el resultado de una función

Efectos y resultados: la instrucción return


Todas las funciones presentadas anteriormente tienen algún tipo de efecto: producen un texto y lo
envían a la consola.

Por supuesto, las funciones, al igual que las funciones matemáticas, pueden tener resultados.

Para lograr que las funciones devuelvan un valor (pero no solo para ese propósito) se utiliza la
instrucción  return  (regresar o retornar).

Esta palabra nos da una idea completa de sus capacidades. Nota: es una palabra reservada de
Python.

La instrucción  return  tiene dos variantes diferentes: considerémoslas por separado.

return sin una expresión


La primera consiste en la palabra reservada en sí, sin nada que la siga.

Cuando se emplea dentro de una función, provoca la terminación inmediata de la ejecución de la


función, y un retorno instantáneo (de ahí el nombre) al punto de invocación.

Nota: si una función no está destinada a producir un resultado, emplear la instrucción  return


no es obligatorio, se ejecutará implícitamente al final de la función.

De cualquier manera, se puede emplear para terminar las actividades de una función, antes de que
el control llegue a la última línea de la función.

Consideremos la siguiente función:

def felizAñoNuevo(deseos = True):

print("Tres ...")
print("Dos ...")

print("Uno ...")

if not deseos:

return

print("¡Feliz año nuevo!")

Cuando se invoca sin ningún argumento:

felizAñoNuevo()

La función produce un poco de ruido; la salida se verá así:

Tres ...

Dos...

Uno...

¡Feliz año nuevo!

Al proporcionar  False  como argumento:

felizAñoNuevo(False)

Se modificará el comportamiento de la función; la instrucción  return  provocará su terminación


justo antes de los deseos. Esta es la salida actualizada:

Tres ...

Dos...

Uno ...

return con una expresión


La segunda variante de  return  está extendida con una expresión:

funcion():

return expresión

Hay dos consecuencias de usarla:


 Provoca la terminación inmediata de la ejecución de la función (nada nuevo en
comparación con la primer variante).
 Además, la función evaluará el valor de la expresión y lo devolverá (de ahí el nombre
una vez mas) como el resultado de la función.

Este ejemplo es sencillo:

def funcion_aburrida():

return 123

x = funcion_aburrida()

print ("La funcion_aburrida ha devuelto su resultado. Es: ", x)

El fragmento de código escribe el siguiente texto en la consola:

La funcion_aburrida ha devuelto su resultado. Es: 123

Vamos a investigarlo.

Analiza la siguiente figura:

La instrucción  return , enriquecida con la expresión (la expresión es muy simple aquí),
"transporta" el valor de la expresión al lugar donde se ha invocado la función.

El resultado se puede usar libremente aquí, por ejemplo, para ser asignado a una variable.

También puede ignorarse por completo y perderse sin dejar rastro.


Ten en cuenta que no estamos siendo muy educados aquí: la función devuelve un valor y lo
ignoramos (no lo usamos de ninguna manera):

def funcion_aburrida():

print("'Modo aburrimiento' ON.")

return 123

print("¡Esta lección es interesante!)

funcion_aburrida()

print("Esta lección es aburrida ...")

El programa produce el siguiente resultado:

¡Esta lección es interesante!

'Modo aburrimiento' ON.

Esta lección es aburrida ...

¿Esta mal? De ninguna manera.

La única desventaja es que el resultado se ha perdido irremediablemente.

No olvides:

 Siempre se te permite ignorar el resultado de la función y estar satisfecho con el efecto


de la función (si la función tiene alguno).
 Si una función intenta devolver un resultado útil, debe contener la segunda variante de la
instrucción  return .

Espera un segundo, ¿Significa esto que también hay resultados inútiles? Sí, en cierto sentido.

Unas pocas palabras acerca de None


Permítenos presentarte un valor muy curioso (para ser honestos, un valor que es
ninguno) llamado  None .

Sus datos no representan valor razonable alguno; en realidad, no es un valor en lo


absoluto; por lo tanto, no debe participar en ninguna expresión.

Por ejemplo, un fragmento de código como el siguiente:


print(None + 2)

Causará un error de tiempo de ejecución, descrito por el siguiente mensaje de


diagnóstico:

TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

Nota:  None  es una palabra reservada.

Solo hay dos tipos de circunstancias en las que  None  se puede usar de manera segura:

 Cuando se le asigna a una variable (o se devuelve como el resultado de una


función).
 Cuando se compara con una variable para diagnosticar su estado interno.

Al igual que aquí:

valor = None
if valor == None:
print("Lo siento, no tienes ningún valor")

No olvides esto: si una función no devuelve un cierto valor utilizando una cláusula de
expresión  return , se asume que devuelve implícitamente  None .

Vamos a probarlo.

Algunas palabras acerca de None: continuación


Echa un vistazo al código en el editor.

Es obvio que la función  strangeFunction  devuelve  True  cuando su argumento es


par.

¿Qué es lo que regresa de otra manera?

Podemos usar el siguiente código para verificarlo:

print(strangeFunction(2))

print(strangeFunction(1))

Esto es lo que vemos en la consola:

True
None

No te sorprendas la próxima vez que veas  None  como el resultado de la función,


puede ser el síntoma de un error sutil dentro de la función.

Efectos y resultados: listas y funciones


Hay dos preguntas adicionales que deben responderse aquí.

El primero es: ¿Se puede enviar una lista a una función como un argumento?

¡Por supuesto que se puede! Cualquier entidad reconocible por Python puede
desempeñar el papel de un argumento de función, aunque debes asegurarte de que la
función sea capaz de hacer uso de él.

Entonces, si pasas una lista a una función, la función tiene que manejarla como una
lista.

Una función como la siguiente:

def sumaDeLista(lst):

sum = 0

for elem in lst:

sum += elem

return sum

y se invoca así:

print(sumaDeLista([5, 4, 3]))

Regresará  12  como resultado, pero habrá problemas si la invocas de esta manera


riesgosa:

print(sumaDeLista(5))

La respuesta de Python será la siguiente:


TypeError: 'int' object is not iterable

Esto se debe al hecho de que el bucle  for  no puede iterar un solo valor entero.

Efectos y resultados: listas y funciones - continuación


La segunda pregunta es: ¿Puede una lista ser el resultado de una función?

¡Si, por supuesto! Cualquier entidad reconocible por Python puede ser un resultado de
función.

Observa el código en el editor. La salida del programa será así:

[4, 3, 2, 1, 0]

Ahora puedes escribir funciones con y sin resultados.

Vamos a profundizar un poco más en los problemas relacionados con las variables en
las funciones. Esto es esencial para crear funciones efectivas y seguras.

LABORATORIO

Tiempo estimado
10-15 minutos

Nivel de dificultad
Fácil

Objetivos
Familiarizar al estudiante con:

 Proyectar y escribir funciones con parámetros.


 Utilizar la sentencia return.
 Probar las funciones.

Escenario
Tu tarea es escribir y probar una función que toma un argumento (un año) y
devuelve  True  si el año es un año bisiesto, o  False  sí no lo es.
Parte del esqueleto de la función ya está en el editor.

Nota: también hemos preparado un breve código de prueba, que puedes utilizar para
probar tu función.

El código utiliza dos listas: una con los datos de prueba y la otra con los resultados
esperados. El código te dirá si alguno de tus resultados no es válido.

def isYearLeap(year):
#
# coloca tu código aquí
#

testData = [1900, 2000, 2016, 1987]


testResults = [False, True, True, False]
for i in range(len(testData)):
yr = testData[i]
print(yr,"->",end="")
result = isYearLeap(yr)
if result == testResults[i]:
print("OK")
else:
print("Error")

def isYearLeap (año) :


if año % 4 != 0:
return False
elif año % 100 != 0:
return True
elif año % 400 != 0:
return False
else:
return True

testData = [1900, 2000, 2016, 1987]


testResults = [False, True, True, False]
for i in range(len(testData)):
yr = testData[i]
print(yr,"->",end="")
result = isYearLeap(yr)
if result == testResults[i]:
print("OK")
else:
print("Failed")

1900 ->OK
2000 ->OK
2016 ->OK
1987 ->OK

LABORATORIO

Tiempo estimado
15-20 minutos

Nivel de dificultad
Medio

Requisitos previos
LABORATORIO 4.1.3.6

Objetivos
Familiarizar al estudiante con:

 Proyectar y escribir funciones parametrizadas.


 Utilizar la sentencia return.
 Utilizar las funciones propias del estudiante.

Escenario
Tu tarea es escribir y probar una función que toma dos argumentos (un año y un mes)
y devuelve el número de días del mes/año dado (mientras que solo febrero es sensible
al valor  year , tu función debería ser universal).

La parte inicial de la función está lista. Ahora, haz que la función devuelva  None  si los
argumentos no tienen sentido.

Por supuesto, puedes (y debes) utilizar la función previamente escrita y probada (LAB
4.1.3.6). Puede ser muy útil. Te recomendamos que utilices una lista con los meses.
Puedea crearla dentro de la función; este truco acortará significativamente el código.
Hemos preparado un código de prueba. Amplíalo para incluir más casos de prueba.

def isYearLeap(year):
#
# tu código del laboratorio anterior
#

def daysInMonth(year, month):


#
# coloca tu código aqui
#

testYears = [1900, 2000, 2016, 1987]


testMonths = [2, 2, 1, 11]
testResults = [28, 29, 31, 30]
for i in range(len(testYears)):
yr = testYears[i]
mo = testMonths[i]
print(yr, mo, "->", end="")
result = daysInMonth(yr, mo)
if result == testResults[i]:
print("OK")
else:
print("Error")

def isYearLeap(year):
if year % 4 != 0:
return False
elif year % 100 != 0:
return True
elif year % 400 != 0:
return False
else:
return True

def daysInMonth(year,month):
if year < 1582 or month < 1 or month > 12:
return None
days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
res = days[month - 1]
if month == 2 and isYearLeap(year):
res = 29
return res

testyears = [1900, 2000, 2016, 1987]


testmonths = [ 2, 2, 1, 11]
testresults = [28, 29, 31, 30]
for i in range(len(testyears)):
yr = testyears[i]
mo = testmonths[i]
print(yr,mo,"-> ",end="")
result = daysInMonth(yr, mo)
if result == testresults[i]:
print("OK")
else:
print("Failed")

LABORATORIO

Tiempo estimado
20-30 minutos

Nivel de dificultad
Medio

Requisitos previos
LAB 4.1.3.6
LAB 4.1.3.7

Objetivos
Familiarizar al estudiante con:

 Proyectar y escribir funciones con parámetros.


 Utilizar la sentencia return.
 Construir un conjunto de funciones de utilidad.
 Utilizar las funciones propias del estudiante.
Escenario
Tu tarea es escribir y probar una función que toma tres argumentos (un año, un mes y
un día del mes) y devuelve el día correspondiente del año, o devuelve  None  si
cualquiera de los argumentos no es válido.

Debes utilizar las funciones previamente escritas y probadas. Agrega algunos casos de
prueba al código. Esta prueba es solo el comienzo.

def isYearLeap(year):
#
# tu código del LAB 4.1.3.6
#

def daysInMonth(year, month):


#
# tu código del LAB 4.1.3.7
#

def dayOfYear(year, month, day):


#
# pon tu código nuevo aquí
#

print(dayOfYear(2000, 12, 31))

def isYearLeap(year):
if year % 4 != 0:
return False
elif year % 100 != 0:
return True
elif year % 400 != 0:
return False
else:
return True

def daysInMonth(year,month):
if year < 1582 or month < 1 or month > 12:
return None
days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
res = days[month - 1]
if month == 2 and isYearLeap(year):
res = 29
return res

testyears = [1900, 2000, 2016, 1987]


testmonths = [ 2, 2, 1, 11]
testresults = [28, 29, 31, 30]
for i in range(len(testyears)):
yr = testyears[i]
mo = testmonths[i]
print(yr,mo,"-> ",end="")
result = daysInMonth(yr, mo)
if result == testresults[i]:
print("OK")
else:
print("Failed")

LABORATORIO

Tiempo estimado
15-20 minutos

Nivel de dificultad
Medio

Objetivos
 Familiarizar al estudiante con nociones y algoritmos clásicos.
 Mejorar las habilidades del estudiante para definir y usar funciones.

Escenario
Un número natural es  primo  si es mayor que 1 y no tiene divisores más que 1 y si mismo.

¿Complicado? De ningúna manera. Por ejemplo, 8 no es un número primo, ya que


puedes dividirlo entre 2 y 4 (no podemos usar divisores iguales a 1 y 8, ya que la
definición lo prohíbe).
Por otra parte, 7 es un número primo, ya que no podemos encontrar ningún divisor
para el.

Tu tarea es escribir una función que verifique si un número es primo o no.

La función:

 Se llama  isPrime .
 Toma un argumento (el valor a verificar).
 Devuelve  True  si el argumento es un número primo, y  False  de lo contrario.

Sugerencia: intenta dividir el argumento por todos los valores posteriores


(comenzando desde 2) y verifica el resto: si es cero, tu número no puede ser un
número primo; analiza cuidadosamente cuándo deberías detener el proceso.

Si necesitas conocer la raíz cuadrada de cualquier valor, puedes utilizar el


operador  ** . Recuerda: la raíz cuadrada de x es la misma que x0.5

Complementa el código en el editor.

Ejecuta tu código y verifica si tu salida es la misma que la nuestra.

Datos de prueba
Salida esperada:

2 3 5 7 11 13 17 19

def isPrime(num):
#
# coloca tu código aquí
#

for i in range(1, 20):


if isPrime(i + 1):
print(i + 1, end=" ")
print()

def isPrime(num):
divisor = 2
while divisor < num:
if num % divisor == 0:
return False
divisor += 1
return True

for i in range(1, 20):


if isPrime(i + 1):
print(i + 1, end=" ")
print()

LABORATORIO

Tiempo estimado
10-15 minutos

Nivel de dificultad
Fácil

Objetivos
 Mejorar las habilidades del estudiante para definir, utilizar y probar funciones.

Escenario
El consumo de combustible de un automóvil se puede expresar de muchas maneras
diferentes. Por ejemplo, en Europa, se muestra como la cantidad de combustible
consumido por cada 100 kilómetros.

En los EE. UU., se muestra como la cantidad de millas recorridas por un automóvil con
un galón de combustible.

Tu tarea es escribir un par de funciones que conviertan l/100km a mpg(milas por


galón), y viceversa.

Las funciones:

 Se llaman  l100kmampg  y  mpgal100km  respectivamente.


 Toman un argumento (el valor correspondiente a sus nombres).

Complementa el código en el editor.

Ejecuta tu código y verifica si tu salida es la misma que la nuestra.


Aquí hay información para ayudarte:

 1 milla = 1609.344 metros.


 1 galón = 3.785411784 litros.

Datos de prueba
Salida esperada:

60.31143162393162

31.36194444444444

23.52145833333333

3.9007393587617467

7.490910297239916

10.009131205673757

def l100kmtompg(liters):
#
# coloca tu código aqui
#

def mpgtol100km(miles):
#
# coloca tu código aqui
#

print(l100kmtompg(3.9))
print(l100kmtompg(7.5))
print(l100kmtompg(10.))
print(mpgtol100km(60.3))
print(mpgtol100km(31.4))
print(mpgtol100km(23.5))

def l100kmtompg(litros):
galones = litros / 3.785411784
millas = 100 * 1000 / 1609.344
return millas / galones

def mpgtol100km(millas):
km100 = millas * 1609.344 / 1000 / 100
litros = 3.785411784
return litros / km100

print(l100kmtompg(3.9))
print(l100kmtompg(7.5))
print(l100kmtompg(10.))
print(mpgtol100km(60.3))
print(mpgtol100km(31.4))
print(mpgtol100km(23.5))

RESUMEN DE LA SECCIÓN

Puntos clave

1. Puedes emplear la palabra clave  return  para decirle a una función que devuelva algún valor. La
instrucción  return  termina la función, por ejemplo:

def multiply(a, b):

return a * b

print(multiply(3, 4)) # salida: 12

def multiply(a, b):

return

print(multiply(3, 4)) # salida: None

2. El resultado de una función se puede asignar fácilmente a una variable, por ejemplo:

def deseos():

return "¡Felíz Cumpleaños!"

d = deseos()
print(d) # salida: ¡Felíz Cumpleaños!

Observa la diferencia en la salida en los siguientes dos ejemplos:

# Ejemplo 1

def deseos():

print("Mis deseos")

return "¡Felíz Cumpleaños!"

deseos() # salida: Mis deseos

# Ejemplo 2

def deseos():

print("Mis Deseos")

return "¡Feliz Cumpleaños!"

print(deseos()) # salidas: Mis Deseos

# ¡Feliz Cumpleaños!

3. Puedes usar una lista como argumento de una función, por ejemplo:

def HolaaTodos(myList):

for nombre in myList:

print("Hola,", nombre)

HolaaTodos(["Adam", "John", "Lucy"])

4. Una lista también puede ser un resultado de función, por ejemplo:

def createList(n):

myList = []

for i in range(n):
myList.append(i)

return myList

print(createList(5))

Ejercicio 1

¿Cuál es la salida del siguiente fragmento de código?

def hola():

return

print("¡Hola!")

hola()

Revisar

La función devolverá un valor  None  implícito

Ejercicio 2

¿Cuál es la salida del siguiente fragmento de código?

def isInt(data):

if type(data) == int:

return True

elif type(data) == float:

return False

print(isInt(5))

print(isInt(5.0))

print(isInt("5"))

Revisar
True

False

None

Ejercicio 3

¿Cuál es la salida del siguiente fragmento de código?

def evenNumLst(ran):

lst = []

for num in range(ran):

if num % 2 == 0:

lst.append(num)

return lst

print(evenNumLst(11))

Revisar

[0, 2, 4, 6, 8, 10]

Ejercicio 4

¿Cuál es la salida del siguiente fragmento de código?

def listUpdater(lst):

updList = []

for elem in lst:

elem **= 2

updList.append(elem)

return updList

l = [1, 2, 3, 4, 5]
print(listUpdater(l))

Revisar

[1, 4, 9, 16, 25]

 4.1.4.1 Los Alcances (Scopes) en Python

Las funciones y sus alcances (scopes)


Comencemos con una definición:

El alcance de un nombre (por ejemplo, el nombre de una variable) es la parte del


código donde el nombre es reconocido correctamente.

Por ejemplo, el alcance del parámetro de una función es la función en si misma. El


parámetro es inaccesible fuera de la función.

Vamos a revisarlo. Observa el código en el editor. ¿Que ocurrirá cuando se ejecute?

El programa no correrá. El mensaje de error dirá:

NameError: name 'x' is not defined

Esto era de esperarse.

Vamos a conducir algunos experimentos para mostrar como es que Python define los
alcances y como los puedes utilizar para tu beneficio.

def scopeTest():

x = 123

scopeTest()

print(x)

Las funciones y sus alcances (scopes): continuación


Comencemos revisando si una variable creada fuera de una función es visible dentro
de una función. En otras palabras, ¿El nombre de la variable se propaga dentro del
cuerpo de la función?

Observa el código en el editor. Ahí esta nuestro conejillo de indias.

def miFuncion():

print("¿Conozco a la variable?", var)

var = 1

miFuncion()

print(var)

El resultado de la prueba es positivo, el código da como salida:

¿Conozco a la variable? 1
1

La respuesta es: una variable que existe fuera de una función tiene alcance
dentro del cuerpo de la función.

Esta regla tiene una excepción muy importante. Intentemos encontrarla.

Hagamos un pequeño cambio al código:

def miFuncion():
var = 2
print("¿Conozco a la variable?", var)

var = 1
miFuncion()
print(var)

El resultado ha cambiado tambiénm el código arroja una salida con una ligera
diferencia:

¿Conozco a la variable? 2
1

¿Qué es lo que ocurrió?


 La variable  var  creada dentro de la función no es la misma que la que se
definió fuera de ella, parece ser que hay dos variables diferentes con el mismo
nombre.
 La variable de la función es una sombra de la variable fuera de la función.

La regla anterior se puede definir de una manera mas precisa y adecuada:

Una variable que existe fuera de una función tiene un alcance dentro del cuerpo
de la función, excluyendo a aquellas que tienen el mismo nombre.

También significa que el alcance de una variable existente fuera de una función
solo se puede implementar dentro de una función cuando su valor es leído. El
asignar un valor hace que la función cree su propia variable.

Asegúrate bien de entender esto correctamente y de realizar tus propios


experimentos.

Las funciones y sus alcances (scopes): la palabra


reservada global
Al llegar a este punto, debemos hacernos la siguiente pregunta: ¿Una función es capaz
de modificar una variable que fue definida fuera de ella? Esto sería muy incomodo.

Afortunadamente, la respuesta es no.

Existe un método especial en Python el cual puede extender el alcance de una


variable incluyendo el cuerpo de las funciones para poder no solo leer los valores
de las variables sino también modificarlos.

Este efecto es causado por la palabra reservada llamada  global :

global name

global name1, name2, ...

El utilizar la palabra reservada dentro de una función con el nombre o nombres de las
variables separados por comas, obliga a Python a abstenerse de crear una nueva
variable dentro de la función; se empleará la que se puede acceder desde el exterior.

En otras palabras, este nombre se convierte en global (tiene un alcance global, y no


importa si se esta leyendo o asignando un valor).

Observa el código en el editor.

def miFuncion():
global var

var = 2

print("¿Conozco a aquella variable?", var)

var = 1

miFuncion()

print(var)

Se ha agregado la palabra  global  a la función.

El código ahora da como salida:

¿Conozco a aquella variable? 2

Esto debe de ser suficiente evidencia para mostrar lo que la palabra


reservada  global  puede hacer.

Como interactúa la función con sus argumentos


Ahora descubramos como la función interactúa con sus argumentos.

El código en editor nos enseña algo. Como puedes observar, la función cambia el valor
de su parámetro. ¿Este cambio afecta el argumento?

Ejecuta el programa y verifícalo.

def miFuncion(n):

print("Yo obtuve", n)

n += 1

print("Yo ahora tengo", n)

var = 1

miFuncion(var)
print(var)

La salida del código es:

Yo obtuve 1
Yo ahora tengo 2
1

La conclusión es obvia - al cambiar el valor del parámetro este no se propaga fuera
de la función (más específicamente, no cuando la variable es un valor escalar, como
en el ejemplo).

Esto también significa que una función recibe el valor del argumento, no el
argumento en sí. Esto es cierto para los valores escalares.

Vale la pena revisar cómo funciona esto con las listas (¿Recuerdas las peculiaridades
de asignar rodajas de listas en lugar de asignar la lista entera?)

El siguiente ejemplo arrojará luz sobre el asunto:

def miFuncion(miLista1):
print(miLista1)
miLista1 = [0, 1]

miLista2 = [2, 3]
miFuncion(miLista2)
print(miLista2)

La salida del código es:

[2, 3]
[2, 3]

Parece ser que se sigue aplicando la misma regla.

La diferencia se puede observar en el siguiente ejemplo:

def miFuncion(miLista1):
print(miLista1)
del miLista1[0]

miLista2 = [2, 3]
miFuncion(miLista2)
print(miLista2)

No se modifica el valor del parámetro  miLista1  (ya se sabe que no afectará el


argumento), en lugar de ello se modificará la lista identificada por el.

El resultado puede ser sorprendente. Ejecuta el código y verifícalo:

[2, 3]
[3]

¿Lo puedes explicar?

Intentémoslo:

 Si el argumento es una lista, el cambiar el valor del parámetro correspondiente


no afecta la lista (Recuerda: las variables que contienen listas son almacenadas
de manera diferente que las escalares).
 Pero si se modifica la lista identificada por el parámetro (Nota: ¡La lista no el
parámetro!), la lista reflejará el cambio.

Es tiempo de escribir algunos ejemplos de funciones. Lo harás en la siguiente sección.

RESUMEN DE SECCIÓN

Puntos Clave

1. Una variable que existe fuera de una función tiene alcance dentro del cuerpo de la función.
(Ejemplo 1) al menos que la función defina una variable con el mismo nombre. (Ejemplo 2, y
Ejemplo 3), por ejemplo:

Ejemplo 1:

var = 2

def multByVar(x):

return x * var

print(multByVar(7)) # salida: 14

Ejemplo 2:
def mult(x):

var = 5

return x * var

print(mult(7)) # salida: 35

Ejemplo 3:

def multip(x):

var = 7

return x * var

var = 3

print(multip(7)) # salida: 49

2. Una variable que existe dentro de una función tiene un alcance solo dentro del cuerpo de la
función (Ejemplo 4), por ejemplo:

Ejemplo 4:

def sum(x):

var = 7

return x + var

print(sum(4)) # salida: 11

print(var) # NameError

3. Se puede emplear la palabra reservada  global  seguida por el nombre de una variable para que el
alcance de la variable sea global, por ejemplo:

var = 2

print(var) # salida: 2

def retVar():

global var
var = 5

return var

print(retVar()) # salida: 5

print(var) # salida: 5

Ejercicio 1

¿Qué ocurrirá cuando se intente ejecutar el siguiente código?

def message():

alt = 1

print("Hola, mundo!")

print(alt)

Revisar

Se arrojará una excepción  NameError ( NameError: name 'alt' is not defined )

Ejercicio 2

¿Cuál es la salida del siguiente fragmento de código?

a = 1

def fun():

a = 2

print(a)

fun()

print(a)
Revisar

Ejercicio 3

¿Cuál es la salida del siguiente fragmento de código?

a = 1

def fun():

global a

a = 2

print(a)

fun()

a = 3

print(a)

Revisar

Ejercicio 4

¿Cuál es la salida del siguiente fragmento de código?

a = 1

def fun():

global a

a = 2
print(a)

a = 3

fun()

print(a)

Revisar

 4.1.5.1 Creando funciones con dos parámetros

Funciones Simples: Calcular el IMC


Definamos una función que calcula el Índice de Masa Corporal (IMC).

Como puedes observar, la formula ocupa dos valores:

 peso (originalmente en kilogramos)


 altura (originalmente en metros)

La nueva función tendrá dos parámetros. Su nombre será  imc , pero si prefieres


utilizar otro nombre, adelante.

Codifiquemos la función.
La función esta completa aquí abajo (y en la ventana de editor):

def imc(peso, altura):


return peso / altura ** 2

print(imc(52.5, 1.65))

El resultado del ejemplo anterior es el siguiente:

19.283746556473833

La función hace lo que deseamos, pero es un poco sencilla - asume que los valores de
ambos parámetros son significativos. Se debe comprobar que son confiables.

Vamos a comprobar ambos y regresar  None  si cualquiera de los dos es incorrecto.

Algunas funciones simples: calcular el IMC y


convertir unidades del sistema inglés al sistema
métrico
Observa el código en el editor. Hay dos cosas a las cuales hay que prestar atención.

def imc(peso, altura):

if altura < 1.0 or altura > 2.5 or \

peso < 20 or peso > 200:

return None

return peso / altura ** 2

print(imc(352.5, 1.65))

Primero, se asegura que los datos que sean ingresados sean correctos, de lo contrario
la salida será:

None

Segundo, observa como el símbolo de diagonal invertida ( \ ) es empleado. Si se


termina una línea de código con el, Python entenderá que la línea continua en la
siguiente.
Esto puede ser útil cuando se tienen largas líneas de código y se desea que sean mas
legibles.

Sin embargo, hay algo que omitimos: las medias en sistema inglés. La función no es
útil para personas que utilicen libras, pies y pulgadas.

¿Qué podemos hacer por ellos?

Escribimos dos funciones sencillas para convertir unidades del sistema inglés al


sistema métrico. Comencemos con las pulgadas.

Es bien conocido que  1 lb = 0.45359237 kg . Esto lo emplearemos en nuestra nueva


función.

Esta función se llamará  lbakg :

def lbakg(lb):

return lb * 0.45359237

print(lbakg(1))

El resultado de la prueba es el siguiente:

0.45359237

Haremos lo mismo ahora con los pies y pulgadas:  1 pie = 0.3048 m , y  1 pulgada =
2.54 cm = 0.0254 m .

La función se llamará  piepulgam :

def piepulgam(pie, pulgada):

return pie * 0.3048 + pulgada * 0.0254

print(piepulgam(1, 1))

El resultado de una prueba rápida es:

0.3302
Resulta como esperado.

Vamos a convertir seis pies a metros:

print(piespulgam(6, 0))

Esta es la salida:

1.8288000000000002

Es muy posible que en ocasiones se desee utilizar solo pies sin pulgadas. ¿Python nos
ayudará? Por supuesto que si.

Se ha modificado el código un poco:

def piepulgam(pie, pulgada = 0.0):

return pie * 0.3048 + pulgada * 0.0254

print(piepulgam(6))

Ahora el parámetro  pulgada  tiene como valor predeterminado el  0.0 .

El código produce la siguiente salida, esto es lo que se esperaba:

1.8288000000000002

Finalmente, el código es capaz de responder a la pregunta: ¿Cual es el IMC de una


persona que tiene 5'7" de altura y un peso de 176 lbs?

Este es el código que debemos de escribir:

def piespulgam(pies, pulgadas = 0.0):

return pies * 0.3048 + pulgadas * 0.0254

def lbsakg(lb):

return lb * 0.45359237
def imc(peso, altura):

if altura < 1.0 or altura > 2.5 or \

peso < 20 or peso > 200:

return None

return peso / altura ** 2

print(imc(peso = lbsakg(176), altura = piespulgam(5, 7)))

La respuesta es:

27.565214082533313

Ejecuta el código y pruébalo.

Algunas funciones simples: continuación


Ahora trabajaremos con triángulos. Comenzaremos con una función que verifique si
tres lados de ciertas longitudes pueden formar un triángulo.
En la escuela aprendimos que la suma arbitraria de dos lados tiene que ser mayor que la
longitud del tercer lado.

No será algo difícil. La función tendrá tres parámetros - uno para cada lado.

Regresará  True  si todos los lados pueden formar un triángulo, y  False  de lo
contrario. En este caso,  esUnTringulo  es un buen nombre para dicha función.

Observa el código en el editor. Ahí se encuentra la función. Ejecuta el programa.

def esUnTriangulo(a, b, c):

if a + b <= c:

return False

if b + c <= a:

return False

if c + a <= b:

return False
return True

print(esUnTriangulo (1, 1, 1))

print(esUnTriangulo (1, 1, 3))

Parece que funciona perfectamente: estos son los resultados:

True

False

¿Se podrá hacer más compacta?. Parece tener demasiadas palabras.

Esta es la versión mas compacta:

def esUnTriangulo(a, b, c):

if a + b <= c or b + c <= a or \

c + a <= b:

return False

return True

print(esUnTriangulo(1, 1, 1))

print(esUnTriangulo(1, 1, 3))

¿Se puede compactar aun mas?

Por supuesto, observa:

def esUnTriangulo (a, b, c):

return a + b > c and b + c > a and c + a > b

print(esUnTriangulo (1, 1, 1))

print(esUnTriangulo (1, 1, 3))

Se ha negado la condición (se invirtieron los operadores relacionales y se


reemplazaron los  or s con  and s, obteniendo una expresión universal para probar
triángulos).
Coloquemos la función en un programa más grande. Se le pedirá al usuario los tres
valores y se hará uso de la función.

Algunas funciones simples: triángulos y el teorema


de Pitágoras
Observa el código en el editor. Le pide al usuario tres valores. Después hace uso de la
función  esUnTriangulo . El código esta listo para correrse.

def esUnTriangulo(a, b, c):

return a + b > c and b + c > a and c + a > b

a = float(input("Ingresa la longitud del primer lado: "))

b = float(input("Ingresa la longitud del segundo lado: "))

c = float(input("Ingresa la longitud del tercer lado: "))

if esUnTriangulo(a, b, c):

print("Felicidades, puede ser un triángulo.")

else:

print("Lo siento, no puede ser un triángulo.")

En el segundo paso, intentaremos verificar si un triángulo es un triángulo rectángulo.

Para ello haremos uso del Teorema de Pitágoras:

c2 = a2 + b2

¿Cómo saber cual de los tres lados es la hipotenusa?

La hipotenusa es el lado mas largo.

Aquí esta el código:

def esUnTriangulo(a, b, c):

return a + b > c and b + c > a and c + a > b

def esUnTrianguloRectangulo(a, b, c):

if not esUnTriangulo (a, b, c):

return False
if c > a and c > b:

return c ** 2 == a ** 2 + b ** 2

if a > b and a > c:

return a ** 2 == b ** 2 + c ** 2

print(esUnTrianguloRectangulo(5, 3, 4))

print(esUnTrianguloRectangulo(1, 3, 4))

Observa como se establece la relación entre la hipotenusa y los dos catetos. Se eligió
el lado mas largo y se aplico el Teorema de Pitágoras para verificar que todo
estuviese en orden. Esto requiere tres revisiones en total.

Algunas funciones simples: evaluando el campo de


un triángulo
También es posible evaluar el campo de un triángulo. La Formula de Heron será útil
aquí:
Vamos a emplear el operador de exponenciación para calcular la raíz cuadrada -
puede ser extraño, pero funciona.

Este es el código resultante:

def esUnTriangulo(a, b, c):

return a + b > c and b + c > a and c + a > b

def heron(a, b, c):

p = (a + b + c) / 2

return (p * (p - a) * (p - b) * (p - c)) ** 0.5

def campoTriangulo(a, b, c):

if not esUnTriangulo(a, b, c):

return None

return heron(a, b, c)

print(campoTriangulo(1., 1., 2. ** .5))

Lo probaremos con un triángulo rectángulo la mitad de un cuadrado y con un lado


igual a 1. Esto significa que su campo debe ser igual a 0.5.

Es extraño pero el código produce la siguiente salida:

0.49999999999999983

Es muy cercano a 0.5, pero no es exactamente 0.5,¿Que significa?, ¿Es un error?


No, no lo es. Son solo los cálculos de valores punto flotantes. Pronto se discutirá el
tema.

Algunas funciones simples: factoriales


La siguiente función a definir calcula factoriales. ¿Recuerdas cómo se calcula un
factorial?

0! = 1 (¡Si!, es verdad.)

1! = 1

2! = 1 * 2

3! = 1 * 2 * 3

4! = 1 * 2 * 3 * 4

n! = 1 * 2 ** 3 * 4 * ... * n-1 * n

Se expresa con un signo de exclamación, y es igual al producto de todos los


números naturales previos al argumento o número dado.

Escribamos el código. Creemos una función con el nombre  factorialFun . Aquí esta el
código:

def factorialFun(n):

if n < 0:

return None

if n < 2:

return 1

producto = 1

for i in range(2, n + 1):

producto *= i

return producto

for n in range(1, 6): # probando


print(n, factorialFun(n))

Observa como se sigue el procedimiento matemático, y como se emplea el


bucle  for  para encontrar el producto.

Estos son los resultados obtenidos de un código de prueba:

1 1

2 2

3 6

4 24

5 120

Algunas funciones simples: Serie Fibonacci


¿Estás familiarizado con la serie Fibonacci?

Son una secuencia de números enteros los cuales siguen una regla sencilla:

 El primer elemento de la secuencia es igual a uno (Fib1 = 1).


 El segundo elemento también es igual a uno (Fib2 = 1).
 Cada numero después de ellos son la suman de los dos números anteriores
(Fibi = Fibi-1 + Fibi-2).

Aquí están algunos de los primeros números en la serie Fibonacci:

fib1 = 1
fib2 = 1
fib3 = 1 + 1 = 2
fib4 = 1 + 2 = 3
fib5 = 2 + 3 = 5
fib6 = 3 + 5 = 8
fib7 = 5 + 8 = 13

¿Que opinas acerca de implementarlo como una función?

Creemos nuestra propia función  fib  y probémosla, aquí esta:

def fib(n):
if n < 1:
return None
if n < 3:
return 1

elem1 = elem2 = 1
sum = 0
for i in range(3, n + 1):
sum = elem1 + elem2
elem1, elem2 = elem2, sum
return sum

for n in range(1, 10): # probando


print(n, "->", fib(n))

Analiza el codigo del bucle  for  cuidadosamente, descifra como se mueven las
variables  elem1  y  elem2  a través de los números subsecuentes de la serie
Fibonacci.

Al probar el código, se generan los siguientes resultados:

1 -> 1
2 -> 1
3 -> 2
4 -> 3
5 -> 5
6 -> 8
7 -> 13
8 -> 21

9 -> 34

Algunas funcione simples: recursividad


Existe algo mas que se desea mostrar: es la recursividad.

Este termino puede describir muchos conceptos distintos, pero uno de ellos, hace
referencia a la programación computacional.

Aquí, la recursividad es una técnica donde una función se invoca a si misma.

Tanto el factorial como la serie Fibonacci, son las mejores opciones para ilustrar este
fenómeno.

La serie de Fibonacci es un claro ejemplo de recursividad.

Fibi = Fibi-1 + Fibi-2

El número ith se refiere al número i-1, y así sucesivamente hasta llegar a los primeros
dos.
¿Puede ser empleado en el código? Por supuesto que puede. Puede hacer el código
mas corto y claro.

La segunda versión de la función  fib()  hace uso directo de la recursividad:

def fib(n):

if n < 1:

return None

if n < 3:

return 1

return fib(n - 1) + fib(n - 2)

El código es mucho mas claro ahora.

¿Pero es realmente seguro?, ¿Implica algún riesgo?

Si, existe algo de riesgo. Si no se considera una condición que detenga las
invocaciones recursivas, el programa puede entrar en un bucle infinito. Se debe
ser cuidadoso.

El factorial también tiene un lado recursivo. Observa:

n! = 1 × 2 × 3 × ... × n-1 × n

Es obvio que:

1 × 2 × 3 × ... × n-1 = (n-1)!

Entonces, finalmente, el resultado es:

n! = (n-1)! × n

Esto se empleará en nuestra nueva solución.

Aquí esta:
def factorialFun(n):

if n < 0:

return None

if n < 2:

return 1

return n * factorialFun(n - 1)

¿Funciona? Claro que si. Pruébalo por ti mismo.

Nuestro viaje funcional esta por terminar. La siguiente sección abordara dos tipos de


datos en Python: tuplas y diccionarios.

 RESUMEN DE SECCIÓN

Puntos Clave

1. Una función puede invocar otras funciones o incluso a sí misma. Cuando una función se invoca a
si misma, se le conoce como recursividad, y la función que se invoca a si misma y contiene una
condición de terminación (la cual le dice a la función que ya no siga invocándose a si misma) es
llamada una función recursiva.

2. Se pueden emplear funciones recursivas en Python para crear funciones limpias, elegantes, y


dividir el código en trozos más pequeños. Sin embargo, se debe tener mucho cuidado ya que
es muy fácil cometer un error y crear una función la cual nunca termine. También se debe
considerar que las funciones recursivas consumen mucha memoria, y por lo tanto pueden ser en
ocasiones ineficientes.

Al emplear la recursividad, se deben de tomar en cuenta tanto sus ventajas como desventajas.

La función factorial es un ejemplo clásico de como se puede implementar el concepto de


recursividad:

# Implementación recursiva de la función factorial

def factorial(n):

if n == 1: # la condición de terminación

return 1

else:
return n * factorial(n - 1)

print(factorial(4)) # 4 * 3 * 2 * 1 = 24

Ejercicio 1

¿Qué ocurrirá al intentar ejecutar el siguiente fragmento de código y porque?

def factorial(n):

return n * factorial(n - 1)

print(factorial(4))

Revisar

La función no tiene una condición de terminación, por lo tanto Python arrojara una excepción
( RecursionError: maximum recursion depth exceeded )

Ejercicio 2

¿Cuál es la salida del siguiente fragmento de código?

def fun(a):

if a > 30:

return 3

else:

return a + fun(a + 3)

print(fun(25))

Revisar

56

 4.1.6.1 Tuplas y diccionarios


Tipos de secuencias y mutabilidad
Antes de comenzar a hablar acerca de tuplas y diccionarios, se deben introducir dos conceptos
importantes: tipos de secuencia y mutabilidad.

Un tipo de secuencia es un tipo de dato en Python el cual es capaz de almacenar mas de un


valor (o ninguno si la secuencia esta vacía), los cuales pueden ser secuencialmente (de ahí el
nombre) examinados, elemento por elemento.

Debido a que el bucle  for  es una herramienta especialmente diseñada para iterar a través de las
secuencias, podemos definirlas de la siguiente manera: una secuencia es un tipo de dato que puede
ser escaneado por el bucle  for .

Hasta ahora, has trabajado con una secuencia en Python, la lista. La lista es un clásico ejemplo de
una secuencia de Python. Aunque existen otras secuencias dignas de mencionar, las cuales se
presentaran a continuación.

La segunda noción - la mutabilidad - es una propiedad de cualquier tipo de dato en Python que
describe su disponibilidad para poder cambiar libremente durante la ejecución de un programa.
Existen dos tipos de datos en Python: mutables e inmutables.

Los datos mutables pueden ser actualizados libremente en cualquier momento, a esta operación
se le denomina "in situ".

In situ es una expresión en Latín que se traduce literalmente como en posición, en el lugar o
momento. Por ejemplo, la siguiente instrucción modifica los datos "in situ":

list.append(1)

Los datos inmutables no pueden ser modificados de esta manera.

Imagina que una lista solo puede ser asignada y leída. No podrías adjuntar ni remover un elemento
de la lista. Si se agrega un elemento al final de la lista provocaría que la lista se cree desde cero.

Se tendría que crear una lista completamente nueva, la cual contenga los elementos ya existentes
mas el nuevo elemento.

El tipo de datos que se desea tratar ahora se llama tupla. Una tupla es una secuencia inmutable. Se
puede comportar como una lista pero no puede ser modificada en el momento.
¿Qué es una tupla?
Lo primero que distingue una lista de una tupla es la sintaxis empleada para crearlas. Las tuplas
utilizan paréntesis, mientras que las listas usan corchetes, aunque también es posible crear una
tupla tan solo separando los valores por comas.

Observa el ejemplo:

tupla1 = (1, 2, 4, 8)

tupla2 = 1., .5, .25, .125

Se definieron dos tuplas, ambas contienen cuatro elementos.

A continuación se imprimen en consola:

print(tupla1)

print(tupla2)

Esto es lo que se muestra en consola:

(1, 2, 4, 8)

(1.0, 0.5, 0.25, 0.125)

Nota: cada elemento de una tupla puede ser de distinto tipo (punto flotante, entero, cadena, etc.).

¿Cómo crear una tupla?


¿Es posible crear una tupla vacía? Si, solo se necesitan unos paréntesis:

tuplaVacia = ()

Si se desea crear una tupla de un solo elemento, se debe de considerar el hecho de que, debido a la
sintaxis (una tupla debe de poder distinguirse de un valor entero ordinario), se debe de colocar una
coma al final:

tuplaUnElemento1 = (1, )

tuplaUnElemento2 = 1.,

El quitar las comas no arruinará el programa en el sentido sintáctico, pero serán variables no tuplas.

¿Cómo utilizar un tupla?


Si deseas leer los elementos de una tupla, lo puedes hacer de la misma manera que se
hace con las listas.

Observa el código en el editor.

miTupla = (1, 10, 100, 1000)

print(miTupla[0])

print(miTupla[-1])

print(miTupla[1:])

print(miTupla[:-2])

for elem in miTupla:

print(elem)

El programa debe de generar la siguiente salida, ejecútalo y comprueba:

1000

(10, 100, 1000)

(1, 10)

10

100

1000

Las similitudes pueden ser engañosas - no intentes modificar en contenido de la


tupla ¡No es una lista!

Todas estas instrucciones (con excepción de primera) causarán un error de ejecución :

miTupla = (1, 10, 100, 1000)

miTupla.append(10000)

del miTupla[0]
miTupla[1] = -10

Este es el mensaje que Python arrojará en la ventana de consola:

AttributeError: 'tuple' object has no attribute 'append'

Cómo utilizar una tupla? continuación


¿Qué más pueden hacer las tuplas?

 La función  len()  acepta tuplas, y regresa el numero de elementos contenidos


dentro.
 El operador  +  puede unir tuplas (ya se ha mostrado esto antes).
 El operador  *  puede multiplicar las tuplas, así como las listas.
 Los operadores  in  y  not in  funcionan de la misma manera que en las listas.

El fragmento de código en el editor presenta todo esto.

miTupla = (1, 10, 100)

t1 = miTupla + (1000, 10000)

t2 = miTupla * 3

print(len(t2))

print(t1)

print(t2)

print(10 in miTupla)

print(-10 not in miTupla)

La salida es la siguiente:

9
(1, 10, 100, 1000, 10000)
(1, 10, 100, 1, 10, 100, 1, 10, 100)
True
True

Una de las propiedades de las tuplas mas útiles es que pueden aparecer en el lado
izquierdo del operador de asignación. Este fenómeno ya se vio con anterioridad,
cuando fue necesario encontrar una manera de intercambiar los valores entre dos
variables.
Observa el siguiente fragmento de código:

var = 123

t1 = (1, )
t2 = (2, )
t3 = (3, var)

t1, t2, t3 = t2, t3, t1

print(t1, t2, t3)

Muestra tres tuplas interactuando en efecto, los valores almacenados en ellas


"circulan" entre ellas.  t1  se convierte en  t2 ,  t2  se convierte en  t3 , y  t3  se convierte
en  t1 .

Nota: el ejemplo presenta un importante hecho mas: los elementos de una tupla


pueden ser variables, no solo literales. Además, pueden ser expresiones si se
encuentran en el lado derecho del operador de asignacion.

¿Qué es un diccionario?
El diccionario es otro tipo de estructura de datos de Python. No es una secuencia (pero puede
adaptarse fácilmente a un procesamiento secuencial) y además es mutable.

Para explicar lo que es un diccionario en Python, es importante comprender de manera literal lo que
es un diccionario.

Un diccionario en Python funciona de la misma manera que un diccionario bilingüe. Por ejemplo,
se tiene la palabra en español "gato" y se necesita su equivalente en francés. Lo que se haría es
buscar en el diccionario para encontrar la palabra "gato". Eventualmente la encontrarás, y sabrás que
la palabra equivalente en francés es "chat".
En el mundo de Python, la palabra que se esta buscando se denomina  clave(key) . La palabra que
se obtiene del diccionario es denominada  valor .

Esto significa que un diccionario es un conjunto de pares de claves y valores. Nota:

 Cada clave debe de ser única. No es posible tener una clave duplicada.


 Una clave puede ser un tipo de dato de cualquier tipo: puede ser un número (entero o
flotante), o incluso una cadena.
 Un diccionario no es una lista. Una lista contiene un conjunto de valores numerados,
mientras que un diccionario almacena pares de valores.
 La función  len()  aplica también para los diccionarios, regresa la cantidad de pares (clave-
valor) en el diccionario.
 Un diccionario es una herramienta de un solo sentido. Si fuese un diccionario español-
francés, podríamos buscar en español para encontrar su contraparte en francés mas no
viceversa.

A continuación veamos algunos ejemplos:

¿Cómo crear un diccionario?


Si deseas asignar algunos pares iniciales a un diccionario, utiliza la siguiente sintaxis:

dict = {"gato" : "chat", "perro" : "chien", "caballo" : "cheval"}


numerosTelefono = {'jefe' : 5551234567, 'Suzy' : 22657854310}

diccionarioVacio = {}

print(dict)

print(numerosTelefono)

print(diccionarioVacio)

En este primer ejemplo, el diccionario emplea claves y valores las cuales ambas son cadenas. En el
segundo, las claves con cadenas pero los valores son enteros. El orden inverso (claves → números,
valores → cadenas) también es posible, así como la combinación número a número.

La lista de todos los pares es encerrada con llaves, mientras que los pares son separados por
comas, y las claves y valores por dos puntos.

El primer diccionario es muy simple, es un diccionario Español-Francés. El segundo es un directorio


telefónico muy pequeño.

Los diccionarios vacíos son construidos por un par vacío de llaves - nada inusual.

El diccionario entero se puede imprimir con una invocación a la función  print() . El fragmento de
código puede producir la siguiente salida:

{'perro': 'chien', 'caballo': 'cheval', 'gato': 'chat'}

{'Suzy': 5557654321, 'boss': 5551234567}

{}

¿Has notado que el orden de los pares impresos es diferente a la asignación inicial?, ¿Qué significa
esto?

Primeramente, recordemos que los diccionarios no son listas - no guardan el orden de sus datos, el
orden no tiene significado (a diferencia de los diccionarios reales). El orden en que un
diccionario almacena sus datos esta fuera de nuestro control. Esto es normal. (*)

NOTA

(*) En Python 3.6x los diccionarios se han convertido en colecciones ordenadas de manera


predeterminada. Tu resultado puede variar dependiendo en la versión de Python que se este
utilizando.
¿Cómo utilizar un diccionario?
Si deseas obtener cualquiera de los valores, debes de proporcionar una clave válida:

print(dict['gato'])
print(numerosTelefono['Suzy'])

El obtener el valor de un diccionario es semejante a la indexación, gracias a los


corchetes alrededor del valor de la clave.

Nota:

 Si una clave es una cadena, se tiene que especificar como una cadena.
 Las claves son sensibles a las mayúsculas y minúsculas:  'Suzy'  sería
diferente a  'suzy' .

El fragmento de código da las siguientes salidas:

dict = {"gato" : "chat", "perro" : "chien", "caballo" : "cheval"}

numerosTelefono = {'jefe' : 5551234567, 'Suzy' : 22657854310}

diccionarioVacio = {}

print(dict['gato'])

print(numerosTelefono['Suzy'])

chat
22657854310

Ahora algo muy importante: No se puede utilizar una clave que no exista. Hacer
algo como lo siguiente:

print(numerosTelefono['presidente'])

Provocará un error de ejecución. Inténtalo.

Afortunadamente, existe una manera simple de evitar dicha situación. El operador  in ,


junto con su acompañante,  not in , pueden salvarnos de esta situación.

El siguiente código busca de manera segura palabras en francés:

dict = {"gato" : "chat", "perro" : "chien", "caballo" : "cheval"}


words = ['gato', 'leon', 'caballo']

for word in words:


if word in dict:
print(word, "->", dict[word])
else:
print(word, "no está en el diccionario")

La salida del código es la siguiente:

gato -> chat


leon no está en el diccionario

caballo -> cheval

¿Cómo utilizar un diccionario? El método keys()


¿Pueden los diccionarios ser examinados utilizando el bucle  for , como las listas o
tuplas?

No y si.

No, porque un diccionario no es un tipo de dato secuencial - el bucle  for  no es útil
aquí.

Si, porque hay herramientas simples y muy efectivas que pueden adaptar cualquier
diccionario a los requerimientos del bucle  for  (en otras palabras, se construye un
enlace intermedio entre el diccionario y una entidad secuencial temporal).

El primero de ellos es un método denominado  keys() , el cual es parte de todo


diccionario. El método retorna o regresa una lista de todas las claves dentro del
diccionario. Al tener una lista de claves se puede acceder a todo el diccionario de una
manera fácil y útil.

A continuación se muestra un ejemplo:

dict = {"gato" : "chat", "perro" : "chien", "caballo" : "cheval"}

for key in dict.keys():

print(key, "->", dict[key]

El código produce la siguiente salida:

caballo -> cheval

perro -> chien


gato -> chat

La función sorted()
¿Deseas que la salida este ordenada? Solo hay que agregar al bucle  for  lo siguiente:

for key in sorted(dict.keys()):

La función  sorted()  hará su mejor esfuerzo y la salida será la siguiente:

caballo -> cheval

gato -> chat

perro -> chien

¿Cómo utilizar un diccionario? Los


métodos item() y values()
Otra manera de hacerlo es utilizar el método  items() . Este método regresa una lista
de tuplas (este es el primer ejemplo en el que las tuplas son mas que un ejemplo de si
mismas) donde cada tupla es un par de cada clave con su valor.

Así es como funciona:

dict = {"gato" : "chat", "perro" : "chien", "caballo" : "cheval"}

for spanish, french in dict.items():

print(spanish, "->", french)

Nota la manera en que la tupla ha sido utilizada como una variable del bucle  for .

El ejemplo imprime lo siguiente:

cat -> chat

dog -> chien

horse -> cheval

También existe un método denominado  values() , funciona de manera muy similar al


de  keys() , pero regresa una lista de valores.
Este es un ejemplo sencillo:

dict = {"gato" : "chat", "perro" : "chien", "caballo" : "cheval"}

for french in dict.values():

print(french)

Como el diccionario no es capaz de automáticamente encontrar la clave de un valor


dado, el rol de este método es algo limitado.

Esta es la salida esperada:

cheval

chien

chat

¿Cómo utilizar un diccionario? Modificar, agregar y


eliminar valores
El asignar un nuevo valor a una clave existente es sencillo, debido a que los
diccionarios son completamente mutables, no existen obstáculos para modificarlos.

Se va a reemplazar el valor  "chat"  por  "minou" , lo cual no es muy adecuado, pero


funcionará con nuestro ejemplo.

Observa:

dict = {"gato" : "chat", "perro" : "chien", "caballo" : "cheval"}

dict['gato'] = 'minou'

print(dict)

La salida es:

{'perro': 'chien', 'caballo': 'cheval', 'gato': 'minou'}


Agregando nuevas claves
El agregar una nueva clave con su valor a un diccionario es tan simple como cambiar
un valor. Solo se tiene que asignar un valor a una nueva clave que no haya existido
antes.

Nota: este es un comportamiento muy diferente comparado a las listas, las cuales no
permiten asignar valores a índices no existentes.

A continuación se agrega un par nuevo al diccionario, un poco extraño pero valido:

dict = {"gato" : "chat", "perro" : "chien", "caballo" : "cheval"}

dict['cisne'] = 'cygne'

print(dict)

El ejemplo muestra como salida:

{'cisne': 'cygne', 'caballo': 'cheval', 'perro': 'chien', 'gato':


'chat'}

EXTRA

También es posible insertar un elemento al diccionario utilizando el


método  update() , por ejemplo:

dict = {"gato" : "chat", "perro" : "chien", "caballo" : "cheval"}

dict.update({"pato" : "canard"})

print(dict)

Eliminado claves
¿Puedes deducir como eliminar una clave de un diccionario?

Nota: al eliminar la clave también se removerá el valor asociado. Los valores no


pueden existir sin sus claves.

Esto se logra con la instrucción  del .


A continuación un ejemplo:

dict = {"gato" : "chat", "perro" : "chien", "caballo" : "cheval"}

del dict['perro']

print(dict)

Nota: el eliminar una clave no existente, provocará un error.

El ejemplo da como salida:

{'gato': 'chat', 'caballo': 'cheval'}

EXTRA

Para eliminar el ultimo elemento de la lista, se puede emplear el método  popitem() :

dict = {"gato" : "chat", "perro" : "chien", "caballo" : "cheval"}

dict.popitem()

print(dict) # outputs: {'gato' : 'chat', 'perro' : 'chien'}

En versiones anteriores de Python, por ejemplo, antes de la 3.6.7, el


método  popitem()  elimina un elemento al azar del diccionario.

Las tuplas y los diccionarios pueden trabajar juntos


Se ha preparado un ejemplo sencillo, mostrando como las tuplas y los diccionarios
pueden trabajar juntos.

Imaginemos el siguiente problema:

 Necesitas un programa para calcular los promedios de tus alumnos.


 El programa pide el nombre del alumno seguido de su calificación.
 Los nombres son ingresados en cualquier orden.
 El ingresar la palabra exit da por terminado el ingreso de nombres.
 Una lista con todos los nombre y el promedio de cada alumno debe ser
mostrada al final.

Observa el código en el editor, se muestra la solución.


grupo = {}

while True:

nombre = input("Ingresa el nombre del estudiante (o exit para detenerse): ")

if nombre == 'exit':

break

calif = int(input("Ingresa la calificación del alumno (0-10): "))

if nombre in grupo:

grupo[nombre] += (calif,)

else:

grupo[nombre] = (calif,)

for nombre in sorted(grupo.keys()):

sum = 0

contador = 0

for calif in grupo[nombre]:

sum += calif

contador += 1

print(nombre, ":", sum / contador)

Ahora se analizará línea por línea:

 Línea 1: crea un diccionario vacío para ingresar los datos: el nombre del
alumno es empleado como clave, mientras que todas las calificaciones
asociadas son almacenadas en una tupla (la tupla puede ser el valor de un
diccionario, esto no es un problema).
 Línea 3: se ingresa a un bucle "infinito" (no te preocupes, saldrémos de el en el
momento indicado).
 Línea 4: se lee el nombre del alumno.
 Línea 5-6: si el nombre es  exit , nos salimos del bucle.
 Línea 8: se pide la calificación del alumno (un valor entero en el rango del 1-
10).
 Línea 10-11: si el nombre del estudiante ya se encuentra en el diccionario, se
alarga la tupla asociada con la nueva calificación (observa el operador +=).
 Línea 12-13: si el estudiante es nuevo (desconocido para el diccionario), se crea
una entrada nueva, su valor es una tupla de un solo elemento la cual contiene
la calificación ingresada.
 Línea 15: se itera a través de los nombres ordenados de los estudiantes.
 Línea 16-17: inicializa los datos necesarios para calcular el promedio (sumador
y contador).
 Línea 18-20: Se itera a través de la tupla, tomado todas las calificaciones
subsecuentes y actualizando la suma junto con el contador.
 Línea 21: se calcula e imprime el promedio del alumno junto con su nombre.

Este es un ejemplo del programa:

Ingresa el nombre del estudiante (o exit para detenerse): Bob


Ingresa la calificación del alumno (0-10): 7
Ingresa el nombre del estudiante (o exit para detenerse): Andy
Ingresa la calificación del alumno (0-10): 3
Ingresa el nombre del estudiante (o exit para detenerse): Bob
Ingresa la calificación del alumno (0-10): 2
Ingresa el nombre del estudiante (o exit para detenerse): Andy
Ingresa la calificación del alumno (0-10): 10
Ingresa el nombre del estudiante (o exit para detenerse): Andy
Ingresa la calificación del alumno (0-10): 3
Ingresa el nombre del estudiante (o exit para detenerse): Bob
Ingresa la calificación del alumno (0-10): 9
Ingresa el nombre del estudiante (o exit para detenerse): exit
Andy : 5.333333333333333

Bob : 6.0

RESUMEN SECCIÓN

Puntos Clave: Tuplas

1. Las Tuplas son colecciones de datos ordenadas e inmutables. Se puede pensar en ellas como listas
inmutables. Se definen con paréntesis:

miTupla = (1, 2, True, "una cadena", (3, 4), [5, 6], None)

print(miTupla)

miLista = [1, 2, True, "una cadena", (3, 4), [5, 6], None]

print(miLista)
Cada elemento de la tupla puede ser de un tipo de dato diferente (por ejemplo, enteros, cadenas,
boleanos, etc.). Las tuplas pueden contener otras tuplas o listas (y viceversa).

2. Se puede crear una tupla vacía de la siguiente manera:

tuplaVacia = ()

print(type(tuplaVacia)) # salida: <class 'tuple'>

3. La tupla de un solo elemento se define de la siguiente manera:

tuplaUnElemento = ("uno", ) # paréntesis y coma

tuplaUnElemento2 = "uno", # sin paréntesis, solo la coma

Si se elimina la coma, Python creará una variable no una tupla:

miTup1 = 1,

print(type(miTup1)) # salida: <class 'tuple'>

miTup2 = 1

print(type(miTup2)) # salida: <class 'int'>

4. Se pueden acceder los elementos de la tupla al indexarlos:

miTupla = (1, 2.0, "cadena", [3, 4], (5, ), True)

print(miTupla[3]) # salida: [3, 4]

5. Las tuplas son inmutables, lo que significa que no se puede agregar, modificar, cambiar o quitar
elementos. El siguiente fragmento de código provocará una excepción:

miTupla = (1, 2.0, "cadena", [3, 4], (5, ), True)

miTupla[2] = "guitarra" # se levanta una excepción TypeError

Sin embargo, se puede eliminar la tupla completa:

miTupla = 1, 2, 3,

del miTupla

print(miTupla) # NameError: name 'miTupla' is not defined


6. Puedes navegar a través de los elementos de una tupla con un bucle (Ejemplo 1), verificar si un
elemento o no esta presente en la tupla (Ejemplo 2), emplear la función  len()  para verificar
cuantos elementos existen en la tupla (Ejemplo 3), o incluso unir o multiplicar tuplas (Ejemplo 4):

# Ejemplo 1

t1 = (1, 2, 3)

for elem in t1:

print(elem)

# Ejemplo 2

t2 = (1, 2, 3, 4)

print(5 in t2)

print(5 not in t2)

# Ejemplo 3

t3 = (1, 2, 3, 5)

print(len(t3))

# Ejemplo 4

t4 = t1 + t2

t5 = t3 * 2

print(t4)

print(t5)

EXTRA

También se puede crear una tupla utilizando la función integrada de Python  tuple() . Esto es
particularmente útil cuando se desea convertir un iterable (por ejemplo, una lista, rango, cadena,
etcétera) en una tupla:

miTup = tuple((1, 2, "cadena"))

print(miTup)
lst = [2, 4, 6]

print(lst) # salida: [2, 4, 6]

print(type(lst)) # salida: <class 'list'>

tup = tuple(lst)

print(tup) # outputs: (2, 4, 6)

print(type(tup)) # salida: <class 'tuple'>

De la misma manera, cuando se desea convertir un iterable en una liste, se puede emplear la función
integrada de Python denominada  list() :

tup = 1, 2, 3,

lst = list(tup)

print(type(lst)) # outputs: <class 'list'>

Puntos Clave: diccionarios

1. Los diccionarios son *colecciones indexadas de datos, mutables y desordenadas. (*En Python


3.6x los diccionarios están ordenados de manera predeterminada.

Cada diccionario es un par de clave : valor. Se puede crear empleado la siguiente sintaxis:

miDictionario = {

clave1 : valor1,

clave2 : valor2,

clave3 : valor3,

2. Si se desea acceder a un elemento del diccionario, se puede hacer haciendo referencia a su clave
colocándola dentro de corchetes (ejemplo 1) o utilizando el método  get()  (ejemplo 2):

polEspDict = {

"kwiat" : "flor",

"woda" : "agua",

"gleba" : "tierra"

}
elemento1 = polEspDict["gleba"] # ejemplo 1

print(elmento1) # salida: tierra

elemento2 = polEspDict.get("woda")

print(elemento2) # salida: agua

3. Si se desea cambiar el valor asociado a una clave especifica, se puede hacer haciendo referencia a
la clave del elemento, a continuación se muestra un ejemplo:

polEspDict = {

"zamek" : "castillo",

"woda" : "agua",

"gleba" : "tierra"

polEspDict["zamek"] = "cerradura"

item = polEspDict["zamek"] # salida: cerradura

4. Para agregar o eliminar una clave (junto con su valor asociado), emplea la siguiente sintaxis:

miDirectorioTelefonico = {} # un diccionario vacio

miDirectorioTelefonico ["Adan"] = 3456783958 # crear o añadir un


par clave-valor

print(miDirectorioTelefonico) # salida: {'Adan': 3456783958}

del miDirectorioTelefonico ["Adan"]

print(miDirectorioTelefonico) # salida: {}

Además, se puede insertar un elemento a un diccionario utilizando el método  update() , y eliminar


el ultimo elemento con el método  popitem() , por ejemplo:

polEspDict = {"kwiat" : "flor"}


polEspDict = update("gleba" : "tierra")

print(polEspDict) # salida: {'kwiat' : 'flor', 'gleba' :


'tierra'}

polEspDict.popitem()

print(polEspDict) # outputs: {'kwiat' : 'flor'}

5. Se puede emplear el bucle  for  para iterar a través del diccionario, por ejemplo:

polEspDict = {

"zamek" : "castillo",

"woda" : "agua",

"gleba" : "tierra"

for item in polEspDict:

print(item) # salida: zamek

# woda

# gleba

6. Si deseas examinar los elementos (claves y valores) del diccionario, puedes emplear el
método  items()  por ejemplo:

polEspDict = {

"zamek" : "castillo",

"woda" : "agua",

"gleba" : "tierra"

for clave, valor in polEspDict.items():

print("Pol/Esp ->", clave, ":", valor)


7. Para comprobar si una clave existe en un diccionario, se puede emplear la palabra reservada  in :

polEspDict = {

"zamek" : "castillo",

"woda" : "agua",

"gleba" : "tierra"

if "zamek" in polEspDict:

print("SI")

else:

print("NO")

8. Se puede emplear la palabra reservada  del  para eliminar un elemento, o un diccionario entero.
Para eliminar todos los elementos de un diccionario se debe emplear el método  clear() :

polEspDict = {

"zamek" : "castillo",

"woda" : "agua",

"gleba" : "tierra"

print(len(polEspDict)) # salida: 3

del polEspDict["zamek"] # elimina un elemento

print(len(polEspDict)) # salida: 2

polEspDict.clear() # elimina todos los elementos

print(len(polEspDict)) # salida: 0

del polEspDict # elimina el diccionario

9. Para copiar un diccionario, emplea el método  copy() :

polEspDict = {
"zamek" : "castillo",

"woda" : "agua",

"gleba" : "tierra"

copyDict = polEspDict.copy()

Puntos Claves: Tuplas y diccionarios

Ejercicio 1

¿Que ocurrirá cuando se intente ejecutar el siguiente código?

miTup = (1, 2, 3)

print(miTup[2])

Revisar

El programa imprimirá  3  en pantalla.

Ejercicio 2

¿Cuál es la salida del siguiente fragmento de código?

tup = 1, 2, 3

a, b, c = tup

print(a * b * c)

Revisar

El programa imprimirá  6  en pantalla. Los elementos de la tupla  tup  han sido "desempaquetados"
en las variables  a ,  b , y  c .

Ejercicio 3
Completa el código para emplear correctamente el método  count()  para encontrar la cantidad
de  2  duplicados en la tupla siguiente.

tup = 1, 2, 3, 2, 4, 5, 6, 2, 7, 2, 8, 9

duplicados = # tu código

print(duplicados) # salida: 4

Revisar

tup = 1, 2, 3, 2, 4, 5, 6, 2, 7, 2, 8, 9

duplicates = tup.count(2)

print(duplicado) # salida: 4

Ejercicio 4

Escribe un programa que "una" los dos diccionarios ( d1  y  d2 ) para crear uno nuevo ( d3 ).

d1 = {'Adam Smith':'A', 'Judy Paxton':'B+'}

d2 = {'Mary Louis':'A', 'Patrick White':'C'}

d3 = {}

for elemento in (d1, d2):

# tu código

print(d3)

Revisar

Solución Muestra:
d1 = {'Adam Smith':'A', 'Judy Paxton':'B+'}

d2 = {'Mary Louis':'A', 'Patrick White':'C'}

d3 = {}
for elemento in (d1, d2):

d3.update(elemento)

print(d3)

Ejercicio 5

Escribe un programa que convierta la lista  l  en una tupla.

l = ["carro", "Ford", "flor", "Tulipán"]

t = # tu código

print(t)

Revisar

Solución muestra:
l = ["carro", "Ford", "flor", "Tulipán"]

t = tuple(l)

print(t)

Ejercicio 6

Escribe un programa que convierta la tupla  colores  en un diccionario.

colores = (("verde", "#008000"), ("azul", "#0000FF"))

# tu código

print(colDict)
Revisar

Solución Muestra:
colores = (("verde", "#008000"), ("azul", "#0000FF"))

colDict = dict(colores)

print(colDict)

Ejercicio 7

¿Que ocurrirá cuando se ejecute el siguiente código?

miDict = {"A":1, "B":2}

copyMiDict = myDict.copy()

miDict.clear()

print(copyMiDict)

Revisar

El programa mostrará  {'A': 1, 'B': 2}  en pantalla

Ejercicio 8

¿Cuál es la salida del siguiente programa?

colores = {

"blanco" : (255, 255, 255),

"gris" : (128, 128, 128),

"rojo" : (255, 0, 0),

"verde" : (0, 128, 0)

for col, rgb in colores.items():


print(col, ":", rgb)

Revisar

blanco : (255, 255, 255)

gris : (128, 128, 128)

rojo : (255, 0, 0)

verde : (0, 128, 0)

 4.1.6.13 PROYECTO: TIC-TAC-TOE


PROYECTO

Tiempo estimado
30-60 minutos

Nivel de dificultad
Medio/difícil

Objetivos
 Perfeccionar las habilidades del estudiante al emplear Python para resolver
problemas complejos.
 La integración de técnicas de programación en un solo programa consistente
de varias partes.

Escenario
Tu tarea es escribir un simple programa que simule jugar a tic-tac-toe (nombre en
inglés) con el usuario. Para hacerlo más fácil, Hemos decidido simplificar el juego.
Aquí están nuestras reglas:

 La maquina (por ejemplo, el programa) jugará utilizando las  'X' s.


 El usuario (por ejemplo, tu) jugará utilizando las  'O' s.
 El primer movimiento es de la maquina: siempre coloca una  'X'  en el centro
del tablero.
 Todos los cuadros están numerados comenzando con el  1  (observa el ejemplo
para que tengas una referencia).
 El usuario ingresa su movimiento introduciendo el numero de cuadro elegido.
El numero debe de ser valido, por ejemplo un valor entero mayor que  0  y
menor que  10 , y no puede ser un cuadro que ya esté ocupado.
 El programa verifica si el juego ha terminado. Existen cuatro posibles
veredictos: el juego continua, el juego termina en empate, tu ganas, o la
maquina gana.
 La maquina responde con su movimiento y se verifica el estado del juego.
 No se debe implementar algún tipo de inteligencia artificial, la maquina elegirá
un cuadro de manera aleatoria, eso es suficiente para este juego.

El ejemplo del programa es el siguiente:

+-------+-------+-------+

| | | |

| 1 | 2 | 3 |

| | | |

+-------+-------+-------+

| | | |

| 4 | X | 6 |

| | | |

+-------+-------+-------+

| | | |

| 7 | 8 | 9 |

| | | |

+-------+-------+-------+

Ingresa tu movimiento: 1

+-------+-------+-------+

| | | |

| O | 2 | 3 |

| | | |

+-------+-------+-------+

| | | |

| 4 | X | 6 |

| | | |

+-------+-------+-------+

| | | |

| 7 | 8 | 9 |
| | | |

+-------+-------+-------+

+-------+-------+-------+

| | | |

| O | X | 3 |

| | | |

+-------+-------+-------+

| | | |

| 4 | X | 6 |

| | | |

+-------+-------+-------+

| | | |

| 7 | 8 | 9 |

| | | |

+-------+-------+-------+

Ingresa tu movimiento: 8

+-------+-------+-------+

| | | |

| O | X | 3 |

| | | |

+-------+-------+-------+

| | | |

| 4 | X | 6 |

| | | |

+-------+-------+-------+

| | | |

| 7 | O | 9 |

| | | |

+-------+-------+-------+

+-------+-------+-------+

| | | |
| O | X | 3 |

| | | |

+-------+-------+-------+

| | | |

| 4 | X | X |

| | | |

+-------+-------+-------+

| | | |

| 7 | O | 9 |

| | | |

+-------+-------+-------+

Ingresa tu movimiento: 4

+-------+-------+-------+

| | | |

| O | X | 3 |

| | | |

+-------+-------+-------+

| | | |

| O | X | X |

| | | |

+-------+-------+-------+

| | | |

| 7 | O | 9 |

| | | |

+-------+-------+-------+

+-------+-------+-------+

| | | |

| O | X | X |

| | | |

+-------+-------+-------+

| | | |
| O | X | X |

| | | |

+-------+-------+-------+

| | | |

| 7 | O | 9 |

| | | |

+-------+-------+-------+

Ingresa tu movimiento: 7

+-------+-------+-------+

| | | |

| O | X | X |

| | | |

+-------+-------+-------+

| | | |

| O | X | X |

| | | |

+-------+-------+-------+

| | | |

| O | O | 9 |

| | | |

+-------+-------+-------+

¡Has Ganado!

Requerimientos
Implementa las siguientes características:

 El tablero debe ser almacenado como una lista de tres elementos, mientras
que cada elemento es otra lista de tres elementos (la lista interna representa
las filas) de manera que todos los cuadros puedas ser accedidos empleado la
siguiente sintaxis:

board[fila][columna]
 Cada uno de los elementos internos de la lista puede contener  'O' ,  'X' , o un
digito representando el número del cuadro (dicho cuadro se considera como
libre).
 La apariencia de tablero debe de ser igual a la presentada en el ejemplo.
 Implementa las funciones definidas para ti en el editor.

Para obtener un valor numérico aleatorio se puede emplear una función integrada de
Python denominada  randrange() . El siguiente ejemplo muestra como utilizarla (El
programa imprime 10 números aleatorios del 1 al 8).

Nota: La instrucción  from-import  provee acceso a la función  randrange  definida en


un módulo externo de Python denominado  random .

from random import randrange

for i in range(10):

print(randrange(8))

def DisplayBoard(board):

print("+-------" * 3,"+",sep="")

for row in range(3):

print("| " * 3,"|",sep="")

for col in range(3):

print("| " + str(board[row][col]) + " ",end="")

print("|")

print("| " * 3,"|",sep="")

print("+-------" * 3,"+",sep="")

def EnterMove(board):

ok = False # assu fake mption - Lo necesitamos para entrar en bucle

while not ok:


move = input("Pon tu movimiento bro: ")

ok = len(move) == 1 and move >= '1' and move <= '9' # Es valida?

if not ok:

print("Jajaja mal movimiento - repite tu entrada!") # no lo es - haz


la entrada nuevamente

continue

move = int(move) - 1 # numero de celda de 0 a 8

row = move // 3 # fila de celda

col = move % 3 # columna de celdas

sign = board[row][col] # verifica el cuadrado seleccionado

ok = sign not in ['O','X']

if not ok: # está ocupado - a la entrada nuevamente

print("Repite tu tiro!")

continue

board[row][col] = 'O' # poner '0' en el cuadrado seleccionado

def MakeListOfFreeFields(board):

free = [] # la lista está vacía inicialmente

for row in range(3): # atraves de las filas

for col in range(3): # atraves de las columnas

if board[row][col] not in ['O','X']: # la fila esta libre?

free.append((row,col)) # yes, it is - agregar nueva tupla a


la lista

return free
def VictoryFor(board,sgn):

if sgn == "X": # Estamos buscando x?

who = 'me' # si es del lado dela computadora

elif sgn == "O": # o para "O"?

who = 'you' # si - es de nuestro lado

else:

who = None # no debemos caer aquí

cross1 = cross2 = True # para diagonales

for rc in range(3):

if board[rc][0] == sgn and board[rc][1] == sgn and board[rc][2] == sgn:


# check row rc

return who

if board[0][rc] == sgn and board[1][rc] == sgn and board[2][rc] == sgn: #


check column rc

return who

if board[rc][rc] != sgn: # checar 1ra lista diagonal

cross1 = False

if board[2 - rc][2 - rc] != sgn: # checar 2da diagonal

cross2 = False

if cross1 or cross2:

return who

return None

import random

def DrawMove(board):
free = MakeListOfFreeFields(board) # crear lista de campos libres

cnt = (len(free))

if cnt > 0: # si la lista no está vacía, elija un lugar para 'X' y configurelo

this = random.randrange(cnt)

row, col = free[this]

board[row][col] = 'X'

board = [ [3 * j + i + 1 for i in range(3)] for j in range(3) ] # crea un tablero vacío

board[1][1] = 'X' # establece primero 'X' en el medio

free = MakeListOfFreeFields(board)

humanturn = True # ¿qué turno es ahora?

while len(free):

DisplayBoard(board)

if humanturn:

EnterMove(board)

victor = VictoryFor(board,'O')

else:

DrawMove(board)

victor = VictoryFor(board,'X')

if victor != None:

break

humanturn = not humanturn

free = MakeListOfFreeFields(board)

DisplayBoard(board)
if victor == 'you':

print("Ganaste!")

elif victor == 'me':

print("Gane")

else:

print("Jajaja!")

 5.1.1.3 Módulos

¿Qué es un módulo?
El código de computadora tiene una tendencia a crecer. Podemos decir que el código que no crece
probablemente sea completamente inutilizable o este abandonado. Un código real, deseado y
ampliamente utilizado se desarrolla continuamente, ya que tanto las demandas de los usuarios como
las expectativas de los usuarios se desarrollan a su propio ritmo.

Un código que no puede responder a las necesidades de los usuarios se olvidará rápidamente y se
reemplazará instantáneamente con un código nuevo, mejor y más flexible. Se debe estar preparado
para esto, y nunca pienses que tus programas están terminados por completo. La finalización es un
estado de transición y generalmente pasa rápidamente, después del primer informe de error. Python
en sí es un buen ejemplo de cómo actúa esta regla.

El código creciente es, de hecho, un problema creciente. Un código más grande siempre significa un
mantenimiento más difícil. La búsqueda de errores siempre es más fácil cuando el código es más
pequeño (al igual que encontrar una rotura mecánica es más simple cuando la maquinaria es más
simple y más pequeña).

Además, cuando se espera que el código que se está creando sea realmente grande (puedes usar el
número total de líneas de código como una medida útil, pero no muy precisa, del tamaño del código)
entonces, se deseará, o más bien, habrá la necesidad de dividirlo en muchas partes, implementado en
paralelo por unos cuantos, una docena, varias docenas o incluso varios cientos de desarrolladores.

Por supuesto, esto no se puede hacer usando un archivo fuente grande, el cual esta siendo editado
por todos los programadores al mismo tiempo. Esto seguramente conducirá a un desastre.

Si se desea que dicho proyecto de software se complete con éxito, se deben tener los medios que le
permitan:
 Se deben dividir todas las tareas entre los desarrolladores.
 Despues, unir todas las partes creadas en un todo funcional.

Por ejemplo, un determinado proyecto se puede dividir en dos partes principales:

 La interfaz de usuario (la parte que se comunica con el usuario mediante widgets y una
pantalla gráfica).
 La lógica (la parte que procesa los datos y produce resultados).

Cada una de estas partes se puede (muy probablemente) dividir en otras más pequeñas, y así
sucesivamente. Tal proceso a menudo se denomina descomposición.

Por ejemplo, si te pidieran organizar una boda, no harías todo tu mismo: encontrarías una serie de
profesionales y dividirías la tarea entre todos.

¿Cómo se divide una pieza de software en partes separadas pero cooperantes? Esta es la
pregunta. Módulos son la respuesta.

¿Cómo hacer uso de un módulo?


El manejo de los módulos consta de dos cuestiones diferentes:

 El primero (probablemente el más común) ocurre cuando se desea utilizar un módulo ya


existente, escrito por otra persona o creado por el programador mismo en algún proyecto
complejo: en este caso, se considera al programador como el usuario del módulo.
 El segundo ocurre cuando se desea crear un nuevo módulo, ya sea para uso propio o para
facilitar la vida de otros programadores: aquí eres el proveedor del módulo.

Discutamos ambas por separado.


En primer lugar, un módulo se identifica por su nombre. Si se desea utilizar cualquier módulo, se
necesita saber su nombre. Se entrega una cantidad (bastante grande) de módulos junto con Python.
Se puede pensar en ellos como una especie de "equipo extra de Python".

Todos estos módulos, junto con las funciones integradas, forman la Biblioteca estándar de
Python - un tipo especial de biblioteca donde los módulos desempeñan el papel de libros (incluso
podemos decir que las carpetas desempeñan el papel de estanterías). Si deseas ver la lista completa
de todos los "volúmenes" recopilados en esa biblioteca, se puede encontrar aquí:
https://docs.python.org/3/library/index.html.

Cada módulo consta de entidades (como un libro consta de capítulos). Estas entidades pueden ser
funciones, variables, constantes, clases y objetos. Si se sabe cómo acceder a un módulo en particular,
se puede utilizar cualquiera de las entidades que almacena.
Comencemos la discusión con uno de los módulos más utilizados, el que lleva por nombre  math . Su
nombre habla por sí mismo: el módulo contiene una rica colección de entidades (no solo funciones)
que permiten a un programador implementar efectivamente cálculos que exigen el uso de funciones
matemáticas, como sen() o log().

Importando un módulo
Para que un módulo sea utilizable, hay que importarlo (piensa en ello como sacar un libro del
estante). La importación de un módulo se realiza mediante una instrucción llamada  import .
Nota:  import  es también una palabra reservada (con todas sus implicaciones).
Supongamos que deseas utilizar dos entidades proporcionadas por el módulo  math :

 Un símbolo (constante) que representa un valor preciso (tan preciso como sea posible
usando aritmética de punto flotante doble) de π (aunque usar una letra griega para nombrar
una variable es totalmente posible en Python, el símbolo se llama pi: es una solución más
conveniente, especialmente para esa parte del mundo que ni tiene ni va a usar un teclado
griego).
 Una función llamada  sin()  (el equivalente informático de la función matemática sine).

Ambas entidades están disponibles a través del módulo  math , pero la forma en que se pueden usar
depende en gran medida de cómo se haya realizado la importación.

La forma más sencilla de importar un módulo en particular es usar la instrucción de importación de


la siguiente manera:

import math

La cláusula contiene:

 La palabra reservada  import .


 El nombre del módulo que se va a importar.

La instrucción puede colocarse en cualquier parte del código, pero debe colocarse antes del primer
uso de cualquiera de las entidades del módulo.

Si se desea (o se tiene que) importar más de un módulo, se puede hacer repitiendo la


cláusula  import , o listando los módulos despues de la palabra reservada  import , por ejemplo:

import math, sys

La instrucción importa dos módulos, primero uno llamado  math  y luego un segundo llamado  sys .

La lista de módulos puede ser arbitrariamente larga.


Importando un módulo: continuación
Para continuar, debes familiarizarte con un término importante: namespace.

No te preocupes, no entraremos en detalles: esta explicación será lo más breve posible.

Un namespace es un espacio (entendido en un contexto no físico) en el que existen algunos nombres


y los nombres no entran en conflicto entre sí (es decir, no hay dos objetos diferentes con el mismo
nombre). Podemos decir que cada grupo social es un namespace - el grupo tiende a nombrar a cada
uno de sus miembros de una manera única (por ejemplo, los padres no darán a sus hijos los mismos
nombres).

Esta singularidad se puede lograr de muchas maneras, por ejemplo, mediante el uso de apodos junto
con los nombres (funcionará dentro de un grupo pequeño como una clase en una escuela) o
asignando identificadores especiales a todos los miembros del grupo (el Seguro Social de EE. UU.
El número es un buen ejemplo de tal práctica).

Dentro de un determinado namespace, cada nombre debe permanecer único. Esto puede
significar que algunos nombres pueden desaparecer cuando cualquier otra entidad de un nombre ya
conocido ingresa al namespace. Mostraremos cómo funciona y cómo controlarlo, pero primero,
volvamos a las importaciones.

Si el módulo de un nombre especificado existe y es accesible (un módulo es de hecho un archivo


fuente de Python), Python importa su contenido, se hacen conocidos todos los nombres definidos
en el módulo, pero no ingresan al namespace del código.

Esto significa que puede tener sus propias entidades llamadas  sin  o  pi  y no serán afectadas en
alguna manera por el import.
En este punto, es posible que te estes preguntando cómo acceder al  pi  el cual viene del
módulo  math .

Para hacer esto, se debe de mandar llamar el  pi  con el su nombre en el módulo original.

Importando un módulo: continuación


Observa el fragmento a continuación, esta es la forma en que se habilitan los nombres
de  pi  y  sin  con el nombre de su módulo de origen:

math.pi
math.sin

Es sencillo, se pone:

 El nombre del módulo ( math ).


 Un punto.
 Y el nombre de la entidad ( pi ).

Tal forma indica claramente el namespace en el que existe el nombre.

Nota: el uso de esto es obligatorio si un módulo ha sido importado con la


instrucción  import . No importa si alguno de los nombres del código y del namespace
del módulo están en conflicto o no.

Este primer ejemplo no será muy avanzado: solo se desea imprimir el valor
de sin(1/2π).

Observa el código en el editor. Así es como se prueba.


import math

print(math.sin(math.pi/2))

El código genera el valor esperado:  1.0 .

Nota: el eliminar cualquiera de las dos indicaciones hará que el código sea erróneo. No
hay otra forma de entrar al namespace de  math  si se hizo lo siguiente:

import math

Importando un módulo: continuación


Ahora te mostraremos como pueden dos namespaces (el tuyo y el del módulo)
coexistir.

Echa un vistazo al ejemplo en la ventana del editor.

import math

def sin(x):

if 2 * x == pi:

return 0.99999999

else:

return None

pi = 3.14

print(sin(pi/2))

print(math.sin(math.pi/2))

Se ha definido una variable y función propia para  pi  y  sin  respectivamente, y se


emplean junto con los de la librería math.

Ejecuta el programa. El código debe producir la siguiente salida:

0.99999999

1.0

Como puedes ver, las entidades no se afectan entre sí.


Importando un módulo: continuación
En el segundo método, la sintaxis del  import  señala con precisión qué entidad (o
entidades) del módulo son aceptables en el código:

from math import pi

La instrucción consta de los siguientes elementos:

 La palabra reservada  from .


 El nombre del módulo a ser (selectivamente) importado.
 La palabra reservada  import .
 El nombre o lista de nombres de la entidad o entidades las cuales estan
siendo importadas al namespace.

La instrucción tiene este efecto:

 Las entidades listadas son las unicas que son importadas del módulo
indicado.
 Los nombres de las entidades importadas pueden ser accedidas dentro del
programa.

Nota: no se importan otras entidades, únicamente las especificadas. Además, no se


pueden importar entidades adicionales utilizando una línea como esta:

print(math.e)

Esto ocasionará un error, ( e  es la constante de Euler: 2.71828...).

Reescribamos el código anterior para incorporar esta nueva técnica.

Aquí esta:

from math import sin, pi

print(sin(pi/2))

El resultado debe de ser el mismo que el anterior, se han empleado las mismas
entidades:  1.0 . Copia y pega el código en el editor, y ejecuta el programa.

¿El código parece más simple? Quizás, pero el aspecto no es el único efecto de este
tipo de importación. Veamos mas a detalle esto.
Importando un módulo: continuación
Observa el código en el editor. Analízalo cuidadosamente:

from math import sin, pi

print(sin(pi/2))

pi = 3.14

def sin(x):

if 2 * x == pi:

return 0.99999999

else:

return None

print(sin(pi/2))

 La línea 01: lleva a cabo la importación selectiva.


 La línea 03: hace uso de las entidades importadas y obtiene el resultado
esperado ( 1.0 ).
 La línea 05 a la 11: redefine el significado de  pi  y  sin  - en efecto, reemplazan
las definiciones originales (importadas) dentro del namespace del código.
 La línea 13: retorna  0.99999999 , lo cual confirma nuestras conclusiones.

Hagamos otra prueba. Observa el código a continuación:

pi = 3.14 # linea 01

def sin(x):
if 2 * x == pi:
return 0.99999999
else:
return None # linea 07

print(sin(pi/2)) # linea 09

from math import sin, pi # linea 12

print(sin(pi/2)) # linea 14

Aquí, se ha invertido la secuencia de las operaciones del código:


 Las líneas del 01 al 07: definen nuestro propio  pi  y  sin .
 La línea 09: hace uso de ellas ( 0.99999999 aparece en pantalla).
 La línea 12: lleva a cabo la importación - los símbolos importados reemplazan
sus definiciones anteriores dentro del namespace.
 La línea 14: retorna  1.0  como resultado.

Importando un Módulo: *
En el tercer método, la sintaxis del  import  es una forma más agresiva que la
presentada anteriormente:

from module import *

Como puedes ver, el nombre de una entidad (o la lista de nombres de entidades) se


reemplaza con un solo asterisco ( * ).

Tal instrucción importa todas las entidades del módulo indicado.

¿Es conveniente? Sí, lo es, ya que libera del deber de enumerar todos los nombres que
se necesiten.

¿Es inseguro? Sí, a menos que conozca todos los nombres proporcionados por el
módulo, es posible que no puedas evitar conflictos de nombres. Trata esto como
una solución temporal e intenta no usarlo en un código regular.

Importando un módulo: la palabra reservada as


Si se importa un módulo y no se esta conforme con el nombre del módulo en
particular (por ejemplo, sí es el mismo que el de una de sus entidades ya definidas)
puede darsele otro nombre: esto se llama aliasing o renombrado.

Aliasing (renombrado) hace que el módulo se identifique con un nombre diferente al


original.

La creación de un alias se realiza junto con la importación del módulo, y exige la


siguiente forma de la instrucción import:

import module as alias

el "module" identifica el nombre del módulo original mientras que el "alias" es el


nombre que se desea usar en lugar del original.
Nota:  as  es una palabra reservada.

Importando un Módulo: continuación


Si necesitas cambiar la palabra  math , puedes introducir tu propio nombre, como en el
ejemplo:

import math as m

print(m.sin(m.pi/2))

Nota: después de la ejecución exitosa de una importación con alias, el nombre


original del módulo se vuelve inaccesible y no debe ser utilizado.

A su vez, cuando usa la variante  from module import name  y se necesita cambiar el
nombre de la entidad, se crea un alias para la entidad. Esto hará que el nombre sea
reemplazado por el alias que se elija.

Así es como se puede hacer:

from module import nombre as alias

Como anteriormente, el nombre original (sin alias) se vuelve inaccesible.

La frase  nombre as alias  puede repetirse: emplea comas para separar las frases,
como a continuación:

from module import n as a, m as b, o as c

El ejemplo puede parecer un poco extraño, pero funciona:

from math import pi as PI, sin as sine

print(sine(PI/2))

Ahora estás familiarizado con los conceptos básicos del uso de módulos. Permítenos
mostrarte algunos módulos y algunas de sus entidades útiles.
Trabajando con módulos estándar
Antes de comenzar a revisar algunos módulos estándar de Python, veamos la función  dir() . No
tiene nada que ver con el comando  dir  de las terminales de Windows o Unix. El
comando  dir()  no muestra el contenido de un directorio o carpeta de disco, pero no se puede negar
que hace algo similar: puede revelar todos los nombres proporcionados a través de un módulo en
particular.

Hay una condición: el módulo debe haberse importado previamente como un todo (es decir, utilizar
la instrucción  import module  -  from module  no es suficiente).

La función devuelve una lista ordenada alfabéticamente la cual contiene todos los nombres de las
entidades disponibles en el módulo:

dir(module)

Nota: Si el nombre del módulo tiene un alias, debe usar el alias, no el nombre original.

Usar la función dentro de un script normal no tiene mucho sentido, pero aún así, es posible.

Por ejemplo, se puede ejecutar el siguiente código para imprimir los nombres de todas las entidades
dentro del módulo  math :

import math

for name in dir(math):

print(name, end="\t")

El código de ejemplo debería producir el siguiente resultado:

__doc__ __loader__ __name__ __package__ __spec__ acos acosh


asin asinh atan atan2 atanh ceil copysign cos cosh
degrees e erf erfc exp expm1 fabs factorial floor
fmod frexp fsum gamma hypot isfinite isinf isnan ldexp
lgamma log log10 log1p log2 modf pi pow radians
sin sinh sqrt tan tanh trunc

¿Has notado los nombres extraños que comienzan con  __  al inicio de la lista? Se hablará más sobre
ellos cuando hablemos sobre los problemas relacionados con la escritura de módulos propios.
Algunos de los nombres pueden traer recuerdos de las lecciones de matemáticas, y probablemente no
tendrás ningún problema en adivinar su significado.

El emplear la función  dir()  dentro de un código puede no parecer muy útil; por lo general, se
desea conocer el contenido de un módulo en particular antes de escribir y ejecutar el código.

Afortunadamente, se puede ejecutar la función directamente en la consola de Python (IDLE), sin


necesidad de escribir y ejecutar un script por separado.

Así es como se puede hacer:

import math

dir(math)

Deberías de ver algo similar a esto:

Funciones seleccionadas del módulo math


Comencemos con una vista previa de algunas de las funciones proporcionadas por el
módulo  math .

Se han elegido algunas arbitrariamente, pero esto no significa que las funciones no
mencionadas aquí sean menos significativas. Tomate el tiempo para revisar las demás
por ti mismo: no tenemos el espacio ni el tiempo para hablar de todas a detalle.

El primer grupo de funciones de módulo  math  están relacionadas con trigonometría:

 sin(x)  → el seno de x.
 cos(x)  → el coseno de x.
 tan(x)  → la tangente de x.
Todas estas funciones toman un argumento (una medida de ángulo expresada en
radianes) y devuelven el resultado apropiado (ten cuidado con  tan()  - no todos los
argumentos son aceptados).

También están sus versiones inversas:

 asin(x)  → el arcoseno de x.
 acos(x)  → el arcocoseno de x.
 atan(x)  → el arcotangente de x.

Estas funciones toman un argumento (verifica que sea correcto) y devuelven una
medida de un ángulo en radianes.

Para trabajar eficazmente con mediciones de ángulos, el módulo  math  proporciona las
siguientes entidades:

 pi  → una constante con un valor que es una aproximación de π.


 radians(x)  → una función que convierte x de grados a radianes.
 degrees(x)  → actuando en el otro sentido (de radianes a grados).

Ahora observa el código en el editor. El programa de ejemplo no es muy sofisticado,


pero ¿puedes predecir sus resultados?

from math import pi, radians, degrees, sin, cos, tan, asin

ad = 90

ar = radians(ad)

ad = degrees(ar)

print(ad == 90.)

print(ar == pi / 2.)

print(sin(ar) / cos(ar) == tan(ar))

print(asin(sin(ar)) == ar)

resultado:

True
True
True
True
Además de las funciones circulares (enumeradas anteriormente), el
módulo  math  también contiene un conjunto de sus análogos hiperbólicos:

 sinh(x)  → el seno hiperbólico.


 cosh(x)  → el coseno hiperbólico.
 tanh(x)  → la tangente hiperbólico.
 asinh(x)  → el arcoseno hiperbólico.
 acosh(x)  → el arcocoseno hiperbólico.
 atanh(x)  → el arcotangente hiperbólico.

Funciones seleccionadas del módulo math:


continuación
Existe otro grupo de las funciones  math  relacionadas con la exponenciación:

 e  → una constante con un valor que es una aproximación del número de Euler
(e).
 exp(x)  → encontrar el valor de ex.
 log(x)  → el logaritmo natural de x.
 log(x, b)  → el logaritmo de x con base b.
 log10(x)  → el logaritmo decimal de x (más preciso que  log(x, 10) ).
 log2(x)  → el logaritmo binario de x (más preciso que  log(x, 2) ).

Nota: la función  pow() :

 pow(x, y)  → encontrar el valor de xy (toma en cuenta los dominios).

Esta es una función incorporada y no se tiene que importar.

Observa el código en el editor. ¿Puedes predecir su salida?

from math import e, exp, log

print(pow(e, 1) == exp(log(e)))

print(pow(2, 2) == exp(2 * log(2)))

print(log(e, e) == exp(0))

resultado:
False
True
True
Funciones seleccionadas del módulo math:
continuación
El último grupo consta de algunas funciones de propósito general como:

 ceil(x)  → devuelve el entero más pequeño mayor o igual que x.


 floor(x)  → el entero más grande menor o igual que x.
 trunc(x)  → el valor de x truncado a un entero (ten cuidado, no es equivalente
a ceil o floor).
 factorial(x)  → devuelve x! (x tiene que ser un valor entero y no negativo).
 hypot(x, y)  → devuelve la longitud de la hipotenusa de un triángulo
rectángulo con las longitudes de los catetos iguales a x e y (lo mismo
que  sqrt(pow(x, 2) + pow(y, 2))  pero más preciso).

Mira el código en el editor. Analiza el programa cuidadosamente.

Demuestra las diferencias fundamentales entre  ceil() ,  floor()  y  trunc() .

Ejecuta el programa y verifica su salida.

from math import ceil, floor, trunc

x = 1.4
y = 2.6

print(floor(x), floor(y))
print(floor(-x), floor(-y))
print(ceil(x), ceil(y))
print(ceil(-x), ceil(-y))
print(trunc(x), trunc(y))
print(trunc(-x), trunc(-y))

resultado:

1 2
-2 -3
2 3
-1 -2
1 2
-1 -2

¿Existe aleatoriedad real en las computadoras?


Otro módulo que vale la pena mencionar es el que se llama  random .

Ofrece algunos mecanismos que permiten operar con números pseudoaleatorios.

Toma en cuenta el prefijo pseudo - los números generados por los módulos pueden parecer
aleatorios en el sentido de que no se pueden predecir, pero no hay que olvidar que todos se calculan
utilizando algoritmos muy refinados.
Los algoritmos no son aleatorios, son deterministas y predecibles. Solo aquellos procesos físicos que
se salgan completamente de nuestro control (como la intensidad de la radiación cósmica) pueden
usarse como fuente de datos aleatorios reales. Los datos producidos por computadoras deterministas
no pueden ser aleatorios de ninguna manera.

Un generador de números aleatorios toma un valor llamado semilla, lo trata como un valor de


entrada, calcula un número "aleatorio" basado en él (el método depende de un algoritmo elegido) y
produce una nueva semilla.

La duración de un ciclo en el que todos los valores semilla son únicos puede ser muy largo, pero no
es infinito: tarde o temprano los valores iniciales comenzarán a repetirse y los valores generadores
también se repetirán. Esto es normal. Es una característica, no un error.

El valor de la semilla inicial, establecido durante el inicio del programa, determina el orden en que
aparecerán los valores generados.

El factor aleatorio del proceso puede ser aumentado al establecer la semilla tomando un número
de la hora actual - esto puede garantizar que cada lanzamiento del programa comience desde un
valor semilla diferente (por lo tanto, usará diferentes números aleatorios).

Afortunadamente, Python realiza dicha inicialización al importar el módulo.

Funciones seleccionadas del módulo random


La función general llamada  random()  (no debe confundirse con el nombre del
módulo) produce un número flotante  x  entre el rango  (0.0, 1.0)  - en otras
palabras: (0.0 <= x < 1.0).

El programa de ejemplo en el editor producirá cinco valores pseudoaleatorios, ya que


sus valores están determinados por el valor semilla (un valor impredecible) actual, no
se pueden adivinar. Ejecuta el programa.
from random import random

for i in range(5):

print(random())

resultado:

0.06810374203512803
0.23333692642900283
0.8574434027740527
0.27805252409069037
0.30368882965548816

La función  seed()  es capaz de establecer la semilla del generador. Te mostraremos


dos de sus variantes:

 seed()  - establece la semilla con la hora actual.


 seed(int_value)  - establece la semilla con el valor entero  int_value .

Hemos modificado el programa anterior; de hecho, hemos eliminado cualquier rastro


de aleatoriedad del código:

from random import random, seed

seed(0)

for i in range(5):
print(random())

Debido al hecho de que la semilla siempre se establece con el mismo valor, la


secuencia de valores generados siempre se ve igual.

Ejecuta el programa. Esto es lo que tenemos:

0.844421851525
0.75795440294
0.420571580831
0.258916750293
0.511274721369

¿Y tú?

Nota: sus valores pueden ser ligeramente diferentes si tu sistema utiliza aritmética de
punto flotante más precisa o menos precisa, pero la diferencia se verá bastante lejos
del punto decimal.
Funciones seleccionadas del módulo random:
continuación
Si deseas valores aleatorios enteros, una de las siguientes funciones encajaría mejor:

 randrange(fin) x
 randrange(inico, fin)
 randrange(inicio, fin, incremento)
 randint(izquierda, derecha)

Las primeras tres invocaciones generarán un número entero tomado


(pseudoaleatoriamente) del rango:

 range(fin)
 range(inicio, fin)
 range(inicio, fin, incremento)

Toma en cuenta la exclusión implícita del lado derecho.

La última función es equivalente a  randrange(izquierda, derecha+1)  - genera el


valor entero  i , el cual cae en el rango [izquierda, derecha] (sin exclusión en el lado
derecho).

Observa el código en el editor. Este programa generará una línea que consta de tres
ceros y un cero o un uno en el cuarto lugar.

from random import randrange, randint

print(randrange(1), end=' ')

print(randrange(0, 1), end=' ')

print(randrange(0, 1, 1), end=' ')

print(randint(0, 1))

resultado:

0 0 0 0

Funciones seleccionadas del módulo random:


continuación
Las funciones anteriores tienen una desventaja importante: pueden producir valores
repetidos incluso si el número de invocaciones posteriores no es mayor que el rango
especificado.
Observa el código en el editor. Es muy probable que el programa genere un conjunto
de números en el que algunos elementos no sean únicos.

from random import randint

for i in range(10):

print(randint(1, 10), end=',')

Esto es lo que se obtiene al ejecutarlo:

9,4,5,4,5,8,9,4,8,4,

Como puedes ver, esta no es una buena herramienta para generar números para la
lotería. Afortunadamente, existe una mejor solución que escribir tu propio código para
verificar la singularidad de los números "sorteados".

Es una función con el nombre de -  choice :

 choice(secuencia)
 sample(secuencia, elementos_a_elegir=1)

La primera variante elige un elemento "aleatorio" de la secuencia de entrada y lo


devuelve.

El segundo crea una lista (una muestra) que consta del


elemento  elementos_a_elegir  (que por defecto es  1 ) "sorteado" de la secuencia de
entrada.

En otras palabras, la función elige algunos de los elementos de entrada, devolviendo


una lista con la elección. Los elementos de la muestra se colocan en orden aleatorio.
Nota que  elementos_a_elegir  no debe ser mayor que la longitud de la secuencia de
entrada.

Observa el código a continuación:

from random import choice, sample

lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

print(choice(lst))
print(sample(lst, 5))
print(sample(lst, 10))

Nuevamente, la salida del programa no es predecible. Nuestros resultados se ven así:


4
[3, 1, 8, 9, 10]
[10, 8, 5, 1, 6, 4, 3, 9, 7, 2]

¿Cómo saber dónde estás?


A veces, puede ser necesario encontrar información no relacionada con Python. Por ejemplo, es
posible que necesites conocer la ubicación de tu programa dentro del entorno de la computadora.

Imagina el entorno de tu programa como una pirámide que consta de varias capas o plataformas.

Las capas son:

 El código (en ejecución) se encuentra en la parte superior.


 Python (mejor dicho, su entorno de ejecución) se encuentra directamente debajo de él.
 La siguiente capa de la pirámide se llena con el SO (sistema operativo): el entorno de
Python proporciona algunas de sus funcionalidades utilizando los servicios del sistema
operativo. Python, aunque es muy potente, no es omnipotente: se ve obligado a usar muchos
ayudantes si va a procesar archivos o comunicarse con dispositivos físicos.
 La capa más inferior es el hardware: el procesador (o procesadores), las interfaces de red,
los dispositivos de interfaz humana (ratones, teclados, etc.) y toda otra maquinaria necesaria
para hacer funcionar la computadora: el sistema operativo sabe cómo emplearlos y utiliza
muchos trucos para trabajar con todas las partes en un ritmo constante.

Esto significa que algunas de las acciones del programa tienen que recorrer un largo camino para
ejecutarse con éxito, imagina que:

 Tu código quiere crear un archivo, por lo que invoca una de las funciones de Python.
 Python acepta la orden, la reorganiza para cumplir con los requisitos del sistema operativo
local (es como poner el sello "aprobado" en una solicitud) y lo envía.
 El SO comprueba si la solicitud es razonable y válida (por ejemplo, si el nombre del archivo
se ajusta a algunas reglas de sintaxis) e intenta crear el archivo. Tal operación,
aparentemente es muy simple, no es atómica: consiste de muchos pasos menores tomados
por...
 El hardware, el cual es responsable de activar los dispositivos de almacenamiento (disco
duro, dispositivos de estado sólido, etc.) para satisfacer las necesidades del sistema
operativo.

Por lo general, no eres consciente de todo ese alboroto: quieres que se cree el archivo y eso es todo.

Pero a veces quieres saber más, por ejemplo, el nombre del sistema operativo que aloja Python y
algunas características que describen el hardware que aloja el sistema operativo.

Hay un módulo que proporciona algunos medios para permitir saber dónde se encuentra y qué
componentes funcionan. El módulo se llama platform. Veamos algunas de las funciones que brinda.

Funciones seleccionadas del módulo platform


El módulo  platform  permite acceder a los datos de la plataforma subyacente, es
decir, hardware, sistema operativo e información sobre la versión del intérprete.

Existe también una función que puede mostrar todas las capas subyacentes en un solo
vistazo, llamada  platform . Simplemente devuelve una cadena que describe el
entorno; por lo tanto, su salida está más dirigida a los humanos que al procesamiento
automatizado (lo veremos pronto).

Así es como se puede invocar:  platform(aliased = False, terse = False) .

Ahora:

 aliased  → cuando se establece a  True  (o cualquier valor distinto de cero)


puede hacer que la función presente los nombres de capa subyacentes
alternativos en lugar de los comunes.
 terse  → cuando se establece a  True  (o cualquier valor distinto de cero) puede
convencer a la función de presentar una forma más breve del resultado (si lo
fuera posible).
Ejecutamos el programa usando tres plataformas diferentes: esto es lo que se obtuvo:

from platform import platform

print(platform())

print(platform(1))

print(platform(0, 1))

Intel x86 + Windows ® Vista (32 bit):

Windows-Vista-6.0.6002-SP2
Windows-Vista-6.0.6002-SP2
Windows-Vista

Intel x86 + Gentoo Linux (64 bit):

Linux-3.18.62-g6-x86_64-Intel-R-_Core-TM-_i3-2330M_CPU_@_2.20GHz-
with-gentoo-2.3
Linux-3.18.62-g6-x86_64-Intel-R-_Core-TM-_i3-2330M_CPU_@_2.20GHz-
with-gentoo-2.3
Linux-3.18.62-g6-x86_64-Intel-R-_Core-TM-_i3-2330M_CPU_@_2.20GHz-
with-glibc2.3.4

Raspberry PI2 + Raspbian Linux (32 bit):

Linux-4.4.0-1-rpi2-armv7l-with-debian-9.0
Linux-4.4.0-1-rpi2-armv7l-with-debian-9.0
Linux-4.4.0-1-rpi2-armv7l-with-glibc2.9

También puedes ejecutar el programa en el IDLE de tu máquina local para verificar


qué salida tendrá.

from platform import platform

print(platform())
print(platform(1))
print(platform(0, 1))

Funciones seleccionadas del módulo platform:


continuación
A veces, es posible que solo se desee conocer el nombre genérico del procesador que
ejecuta el sistema operativo junto con Python y el código, una función
llamada  machine()  te lo dirá. Como anteriormente, la función devuelve una cadena.
Nuevamente, ejecutamos el programa en tres plataformas diferentes:

from platform import machine

print(machine())

Intel x86 + Windows ® Vista (32 bit):

x86

Intel x86 + Gentoo Linux (64 bit):

x86_64

Raspberry PI2 + Raspbian Linux (32 bit):

armv7l

Funciones seleccionadas del módulo platform:


continuación
La función  processor()  devuelve una cadena con el nombre real del procesador (si lo
fuese posible).

Una vez más, ejecutamos el programa en tres plataformas diferentes:

from platform import processor

print(processor())

Intel x86 + Windows ® Vista (32 bit):

x86

Intel x86 + Gentoo Linux (64 bit):

Intel(R) Core(TM) i3-2330M CPU @ 2.20GHz

Raspberry PI2 + Raspbian Linux (32 bit):

armv7l
Funciones seleccionadas del módulo platform:
continuación
Una función llamada  system()  devuelve el nombre genérico del sistema operativo en
una cadena.

Nuestras plataformas de ejemplo se presentaron así:

from platform import system

print(system())

Intel x86 + Windows ® Vista (32 bit):

Windows

Intel x86 + Gentoo Linux (64 bit):

Linux

Raspberry PI2 + Raspbian Linux (32 bit):

Linux

Funciones seleccionadas del módulo platform:


continuación
La versión del sistema operativo se proporciona como una cadena por la
función  version() .

Ejecuta el código y verifica su salida. Esto es lo que tenemos:

from platform import versión

print(version())

Intel x86 + Windows ® Vista (32 bit):

6.0.6002

Intel x86 + Gentoo Linux (64 bit):


#1 SMP PREEMPT Fri Jul 21 22:44:37 CEST 2017

Raspberry PI2 + Raspbian Linux (32 bit):

#1 SMP Debian 4.4.6-1+rpi14 (2016-05-05)

Funciones seleccionadas del módulo platform:


continuación
Si necesitas saber qué versión de Python está ejecutando tu código, puedes verificarlo
utilizando una serie de funciones dedicadas, aquí hay dos de ellas:

 python_implementation()  → devuelve una cadena que denota la


implementación de Python (espera  CPython  aquí, a menos que decidas utilizar
cualquier rama de Python no canónica).
 python_version_tuple()  → devuelve una tupla de tres elementos la cual
contiene:
o la parte mayor de la versión de Python.
o la parte menor,
o el número de nivel del patch.

Nuestro programa ejemplo produjo el siguiente resultado:

from platform import python_implementation, python_version_tuple

print(python_implementation())

for atr in python_version_tuple():

print(atr)

CPython

3
6
4

Es muy probable que tu versión de Python sea diferente.

Índice del Módulo de Python


Aquí solo hemos cubierto los conceptos básicos de los módulos de Python. Los
módulos de Python conforman su propio universo, en el que Python es solo una
galaxia, y nos aventuraríamos a decir que explorar las profundidades de estos
módulos puede llevar mucho más tiempo que familiarizarse con Python "puro".
Además, la comunidad de Python en todo el mundo crea y mantiene cientos de
módulos adicionales utilizados en aplicaciones muy específicas como la genética, la
psicología o incluso la astrología.

Estos módulos no están (y no serán) distribuidos junto con Python, o a través de


canales oficiales, lo que hace que el universo de Python sea más amplio, casi infinito.

Puedes leer sobre todos los módulos estándar de Python


aquí: https://docs.python.org/3/py-modindex.html.

No te preocupes, no necesitarás todos estos módulos. Muchos de ellos son muy


específicos.

Todo lo que se necesita hacer es encontrar los módulos que se desean y aprender a
cómo usarlos. Es fácil.

 5.1.3.1 Módulos y Paquetes

¿Qué es un paquete?
Escribir tus propios módulos no difiere mucho de escribir scripts comunes.

Existen algunos aspectos específicos que se deben tomar en cuenta, pero definitivamente no es algo
complicado. Lo verás pronto.

Resumamos algunos aspectos importantes:


 Un módulo es un contenedor lleno de funciones - puedes empaquetar tantas funciones
como desees en un módulo y distribuirlo por todo el mundo.
 Por supuesto, no es una buena idea mezclar funciones con diferentes áreas de aplicación
dentro de un módulo (al igual que en una biblioteca: nadie espera que los trabajos científicos
se incluyan entre los cómics), así que se deben agrupar las funciones cuidadosamente y
asignar un nombre claro e intuitivo al módulo que las contiene (por ejemplo, no le des el
nombre  videojuegos  a un módulo que contiene funciones destinadas a particionar y
formatear discos duros).
 Crear muchos módulos puede causar desorden: tarde que temprano querrás agrupar tus
módulos de la misma manera que previamente has agrupado funciones: ¿Existe un
contenedor más general que un módulo?.
 Sí lo hay, es un paquete: en el mundo de los módulos, un paquete juega un papel similar al
de una carpeta o directorio en el mundo de los archivos.

Tu primer módulo
En esta sección, trabajarás localmente en tu máquina. Comencemos desde cero, de la siguiente
manera:

Se necesitan dos archivos para realizar estos experimentos. Uno de ellos será el módulo en sí. Está
vacío ahora. No te preocupes, lo vas a llenar con el código real.

El archivo lleva por nombre module.py. No muy creativo, pero es simple y claro.

El segundo archivo contiene el código usando el nuevo módulo. Su nombre es main.py.

Su contenido es muy breve hasta ahora:


Nota: ambos archivos deben estar ubicados en la misma carpeta. Te recomendamos crear una
carpeta nueva y vacía para ambos archivos. Esto hará que las cosas sean más fáciles.

Inicia el IDLE y ejecuta el archivo main.py.¿Que ves?

No deberías ver nada. Esto significa que Python ha importado con éxito el contenido del
archivo module.py. No importa que el módulo esté vacío por ahora. El primer paso ya está hecho,
pero antes de dar el siguiente paso, queremos que eches un vistazo a la carpeta en la que se
encuentran ambos archivos.

¿Notas algo interesante?

Ha aparecido una nueva subcarpeta, ¿puedes verla? Su nombre es __pycache__. Echa un vistazo
adentro. ¿Que ves?

Hay un archivo llamado (más o menos) module.cpython-xy.pyc donde x y y son dígitos


derivados de tu versión de Python (por ejemplo, serán 3 y 4 si utilizas Python 3.4).

El nombre del archivo es el mismo que el de tu módulo. La parte posterior al primer punto dice qué
implementación de Python ha creado el archivo (CPython) y su número de versión. La ultima parte
(pyc) viene de las palabras Python y compilado.

Puedes mirar dentro del archivo: el contenido es completamente ilegible para los humanos. Tiene
que ser así, ya que el archivo está destinado solo para uso de Python.

Cuando Python importa un módulo por primera vez, traduce el contenido a una forma "semi"
compilada. El archivo no contiene código en lenguaje máquina: es código semi-compilado interno
de Python, listo para ser ejecutado por el intérprete de Python. Como tal archivo no requiere tantas
comprobaciones como las de un archivo fuente, la ejecución comienza más rápido y también se
ejecuta más rápido.
Gracias a eso, cada importación posterior será más rápida que interpretar el código fuente desde
cero.

Python puede verificar si el archivo fuente del módulo ha sido modificado (en este caso, el
archivo pyc será reconstruido) o no (cuando el archivo pyc pueda ser ejecutado al instante). Este
proceso es completamente automático y transparente, no se tiene que estar tomando en cuenta.

Tu primer módulo: continuación


Ahora hemos puesto algo en el archivo del módulo:

¿Puedes notar alguna diferencia entre un módulo y un script ordinario? No hay ninguna hasta ahora.

Es posible ejecutar este archivo como cualquier otro script. Pruébalo por ti mismo.

¿Que es lo que pasa? Deberías de ver la siguiente línea dentro de tu consola:

Me gusta ser un módulo.

Volvamos al archivo main.py:
Ejecuta el archivo. ¿Que ves? Con suerte, verás algo como esto:

Me gusta ser un módulo.

¿Qué significa realmente?

Cuando un módulo es importado, su contenido es ejecutado implícitamente por Python. Le da al


módulo la oportunidad de inicializar algunos de sus aspectos internos (por ejemplo, puede asignar a
algunas variables valores útiles). Nota: la inicialización se realiza solo una vez, cuando se produce
la primera importación, por lo que las asignaciones realizadas por el módulo no se repiten
innecesariamente.

Imagina el siguiente contexto:

 Existe un módulo llamado mod1.


 Existe un módulo llamado mod2 el cual contiene la instrucción  import mod1 .
 Hay un archivo principal que contiene las instrucciones  import mod1  y  import mod2 .

A primera vista, se puede pensar que mod1 será importado dos veces - afortunadamente, solo se


produce la primera importación. Python recuerda los módulos importados y omite
silenciosamente todas las importaciones posteriores.

Python puede hacer mucho más. También crea una variable llamada  __name__ .

Además, cada archivo fuente usa su propia versión separada de la variable, no se comparte entre
módulos.
Te mostraremos cómo usarlo. Modifica el módulo un poco:

Ver código en Sandbox

Ahora ejecuta el archivo module.py. Deberías ver las siguientes líneas:

Me gusta ser un módulo.

__main__

Ahora ejecuta el archivo main.py. ¿Y? ¿Ves lo mismo que nosotros?

Me gusta ser un módulo.

module

Podemos decir que:

 Cuando se ejecuta un archivo directamente, su variable  __name__  se establece


a  __main__ .
 Cuando un archivo se importa como un módulo, su variable  __name__  se establece al
nombre del archivo (excluyendo a .py).

Así es como puedes hacer uso de la variable  __main__  para detectar el contexto en el cual se
activó tu código:
Ver código en Sandbox

Sin embargo, hay una forma más inteligente de utilizar la variable. Si escribes un módulo lleno de
varias funciones complejas, puedes usarla para colocar una serie de pruebas para verificar si las
funciones trabajan correctamente.

Cada vez que modifiques alguna de estas funciones, simplemente puedes ejecutar el módulo para
asegurarte de que sus enmiendas no estropeen el código. Estas pruebas se omitirán cuando el código
se importe como un módulo.

Tu primer módulo: continuación


Este módulo contendrá dos funciones simples, y si deseas saber cuántas veces se han invocado las
funciones, necesitas un contador inicializado en cero cuando se importa el módulo. Puedes hacerlo
de esta manera:

Ver código en Sandbox

El introducir tal variable es absolutamente correcto, pero puede causar importantes efectos


secundarios que debes tener en cuenta.
Analiza el archivo modificado main.py:

Ver código en Sandbox

Como puedes ver, el archivo principal intenta acceder a la variable de contador del módulo. ¿Es esto
legal? Sí lo es. ¿Es utilizable? Claro. ¿Es seguro? Eso depende: si confías en los usuarios de tu
módulo, no hay problema; sin embargo, es posible que no desees que el resto del mundo vea
tu variable personal o privada.

A diferencia de muchos otros lenguajes de programación, Python no tiene medios para permitirte
ocultar tales variables a los ojos de los usuarios del módulo. Solo puedes informar a tus usuarios que
esta es tu variable, que pueden leerla, pero que no deben modificarla bajo ninguna circunstancia.

Esto se hace anteponiendo al nombre de la variable  _  (un guión bajo) o  __  (dos guiones bajos), pero
recuerda, es solo un acuerdo. Los usuarios de tu módulo pueden obedecerlo o no.

Nosotros por supuesto, lo respetaremos. Ahora pongamos dos funciones en el módulo: evaluarán la
suma y el producto de los números recopilados en una lista.

Además, agreguemos algunos adornos allí y eliminemos los restos superfluos.

El módulo está listo:


Ver código en Sandbox

Algunos elementos necesitan explicación:

 La línea que comienza con  #!  desde el punto de vista de Python, es solo


un comentario debido a que comienza con  # . Para sistemas operativos Unix y similares a
Unix (incluido MacOS), dicha línea indica al sistema operativo cómo ejecutar el
contenido del archivo (en otras palabras, qué programa debe lanzarse para interpretar el
texto). En algunos entornos (especialmente aquellos conectados con servidores web) la
ausencia de esa línea causará problemas.
 Una cadena (quizás una multilínea) colocada antes de las instrucciones de cualquier módulo
(incluidas las importaciones) se denomina doc-string, y debe explicar brevemente el
propósito y el contenido del módulo.
 Las funciones definidas dentro del módulo ( suml()  y  prodl() ) están disponibles para
ser importadas.
 Se ha utilizado la variable  __name__  para detectar cuándo se ejecuta el archivo de forma
independiente.
Ahora es posible usar el nuevo módulo, esta es una forma de hacerlo:

Ver código en Sandbox

Tu primer módulo: continuación


Es hora de hacer este ejemplo más complejo: hemos asumido aquí que el archivo Python principal se
encuentra en la misma carpeta o directorio que el módulo que se va a importar.

Realicemos el siguiente experimento mental:

 Estamos utilizando el sistema operativo Windows ® (esta suposición es importante, ya que


la forma del nombre del archivo depende de ello).
 El script principal de Python se encuentra en C:\Users\user\py\progs y se
llama main.py.
 El módulo a importar se encuentra en C:\Users\user\py\modules .
¿Como lidiar con ello?

Para responder a esta pregunta, tenemos que hablar sobre cómo Python busca módulos. Hay una
variable especial (en realidad una lista) que almacena todas las ubicaciones (carpetas o directorios)
que se buscan para encontrar un módulo que ha sido solicitado por la instrucción import.

Python examina estas carpetas en el orden en que aparecen en la lista: si el módulo no se puede
encontrar en ninguno de estos directorios, la importación falla.

De lo contrario, se tomará en cuenta la primera carpeta que contenga un módulo con el nombre
deseado (si alguna de las carpetas restantes contiene un módulo con ese nombre, se ignorará).

La variable se llama  path  (ruta), y es accesible a través del módulo llamado  sys . Así es como
puedes verificar su valor:

Hemos lanzado el código dentro del directorio C:\User\user y obtenemos:

C:\Users\user

C:\Users\user\AppData\Local\Programs\Python\Python36-32\
python36.zip

C:\Users\user\AppData\Local\Programs\Python\Python36-32\DLLs

C:\Users\user\AppData\Local\Programs\Python\Python36-32\lib

C:\Users\user\AppData\Local\Programs\Python\Python36-32
C:\Users\user\AppData\Local\Programs\Python\Python36-32\lib\site-
packages

Nota: la carpeta en la que comienza la ejecución aparece en el primer elemento de la ruta.

Ten en cuenta también que: hay un archivo zip listado como uno de los elementos de la ruta, esto no
es un error. Python puede tratar los archivos zip como carpetas ordinarias, esto puede ahorrar mucho
almacenamiento.

¿Puedes predecir cómo resolver este problema?

Puedes resolverlo agregando una carpeta que contenga el módulo a la variable de ruta (path
variable), es completamente modificable.

Una de las varias soluciones posibles se ve así:

Ver código en Sandbox

Nota:

 Se ha duplicado la  \  dentro del nombre de la carpeta, ¿sabes por qué?


 Debido a que una diagonal invertida se usa para escapar de otros caracteres, si
deseas obtener solo una diagonal invertida, debe escapar.

 Hemos utilizado el nombre relativo de la carpeta: esto funcionará si inicia el


archivo main.py directamente desde su carpeta de inicio, y no funcionará si el directorio
actual no se ajusta a la ruta relativa; siempre puedes usar una ruta absoluta, como esta:

path.append('C:\\Users\\user\\py\\modules')

 Hemos usado el método  append() , la nueva ruta ocupará el último elemento en la lista de
rutas; si no te gusta la idea, puedes usar en lugar de ello el método  insert() .

Tu primer paquete
Imagina que en un futuro no muy lejano, tu y tus socios escriben una gran cantidad de funciones en
Python.

Tu equipo decide agrupar las funciones en módulos separados, y este es el resultado final:
Nota: hemos presentado todo el contenido solo para el módulo omega: supongamos que todos los
módulos tienen un aspecto similar (contienen una función denominada  funX , donde  X  es la primera
letra del nombre del módulo).
De repente, alguien se da cuenta de que estos módulos forman su propia jerarquía, por lo que
colocarlos a todos en una estructura plana no será una buena idea.

Después de algo de discusión, el equipo llega a la conclusión de que los módulos deben agruparse.
Todos los participantes están de acuerdo en que la siguiente estructura de árbol refleja perfectamente
las relaciones mutuas entre los módulos:

Repasemos esto de abajo hacia arriba:

 El grupo ugly contiene dos módulos: psi y omega.


 El grupo best contiene dos módulos: sigma y tau.
 El grupo good contiene dos módulos: (alpha y beta) y un subgrupo (best).
 El grupo extra contiene dos subgrupos: (good y bad) y un módulo (iota).

¿Se ve mal? De ninguna manera: analiza la estructura cuidadosamente. Se parece a algo, ¿no?
Parece la estructura de un directorio.

Tu primer paquete: continuación


Así es como se ve actualmente la relación entre módulos:

Tal estructura es casi un paquete (en el sentido de Python). Carece del detalle fino para ser funcional
y operativo. Lo completaremos en un momento.

Si asumes que extra es el nombre de un recientemente creado paquete (piensa en el como la raíz


del paquete), impondrá una regla de nomenclatura que te permitirá nombrar claramente cada
entidad del árbol.

Por ejemplo:

 La ubicación de una función llamada  funT()  del paquete tau puede describirse como:

extra.good.best.tau.funT()
 Una función marcada como:
extra.ugly.psi.funP()
proviene del módulo psi el cual esta almacenado en subpaquete ugly del paquete extra.

Se deben responder dos preguntas:


 ¿Cómo se transforma este árbol (en realidad, un subárbol) en un paquete real de Python (en
otras palabras, ¿cómo convence a Python de que dicho árbol no es solo un montón de
archivos basura, sino un conjunto de módulos)?
 ¿Dónde se coloca el subárbol para que Python pueda acceder a él?

La primer pregunta tiene una respuesta sorprendente: los paquetes, como los módulos, pueden
requerir inicialización.

La inicialización de un módulo se realiza mediante un código independiente (que no forma parte de


ninguna función) ubicado dentro del archivo del módulo. Como un paquete no es un archivo, esta
técnica es inútil para inicializar paquetes.

En su lugar, debes usar un truco diferente: Python espera que haya un archivo con un nombre muy
exclusivo dentro de la carpeta del paquete:  __init__.py .

El contenido del archivo se ejecuta cuando se importa cualquiera de los módulos del paquete. Si no
deseas ninguna inicialización especial, puedes dejar el archivo vacío, pero no debes omitirlo.

Tu primer paquete: continuación


La presencia del archivo __init.py__ finalmente compone el paquete:
Nota: no solo la carpeta raiz puede contener el archivo __init.py__ - también puedes ponerlo
dentro de cualquiera de sus subcarpetas (subpaquetes). Puede ser útil si algunos de los subpaquetes
requieren tratamiento individual o un tipo especial de inicialización.

Ahora es el momento de responder la segunda pregunta: la respuesta es simple: donde quiera. Solo


tienes que asegurarte de que Python conozca la ubicación del paquete. Ya sabes cómo hacer eso.

Estás listo para usar tu primer paquete.

Supongamos que el entorno de trabajo se ve de la siguiente manera:

Hemos preparado un archivo zip que contiene todos los archivos de la rama de paquetes. Puedes
descargarlo y usarlo para tus propios experimentos, pero recuerda desempaquetarlo en la carpeta
presentada en el esquema, de lo contrario, no será accesible para el código.

DESCARGAR   Archivo ZIP Módulos y paquetes

Continuarás tus experimentos usando el archivo main2.py.


Tu primer paquete: continuación
Vamos a acceder a la función  funI()  del módulo iota del paquete extra. Nos obliga a usar
nombres de paquetes calificados (asocia esto al nombramiento de carpetas y subcarpetas).

Asi es como se hace:

Ver código Sandbox

Nota:

 Hemos modificado la variable  path  para que sea accesible a Python.


 El  import  no apunta directamente al módulo, pero especifica la ruta completa desde la
parte superior del paquete.

El reemplazar  import extra.iota  con  import iota  causará un error.

La siguiente variante también es válida:

Ver código en Sandbox

Nota el nombre calificado del módulo iota.


Tu primer paquete: continuación
Ahora vamos hasta el final del árbol: así es como se obtiene acceso a los módulos sigma y tau.

Ver código en Sandbox

Puedes hacer tu vida más fácil usando un alias:

Ver código en Sandbox

Supongamos que hemos comprimido todo el subdirectorio, comenzando desde la


carpeta extra ( incluyéndola), y se obtuvo un archivo llamado extrapack.zip. Después,
colocamos el archivo dentro de la carpeta packages.

Ahora podemos usar el archivo zip en un rol de paquetes:


Ver código en Sandbox

Si deseas realizar tus propios experimentos con el paquete que hemos creado, puedes descargarlo a
continuación. Te alentamos a que lo hagas.

DESCARGAR   Archivo ZIP Extrapack

Ahora puedes crear módulos y combinarlos en paquetes. Es hora de comenzar una discusión
completamente diferente: sobre errores y fallas.

Errores, fallas y otras plagas.


Cualquier cosa que pueda salir mal, saldrá mal.

Esta es la ley de Murphy, y funciona en todo y siempre. Si la ejecución del código


puede salir mal, lo hará.

import math

x = float(input("Ingresa x: "))

y = math.sqrt(x)

print("La raíz cuadrada de", x, "es igual a", y)

Observa el código en el editor. Hay al menos dos formas posibles de que "salga mal" la
ejecución. ¿Puedes verlas?

 Como el usuario puede ingresar una cadena de caracteres completamente


arbitraria, no hay garantía de que la cadena se pueda convertir en un valor
flotante - esta es la primera vulnerabilidad del código.
 La segunda es que la función  sqrt()  fallará si se le ingresa un valor
negativo.
Puedes recibir alguno de los siguientes mensajes de error.

Algo como esto:

Ingresa x: Abracadabra

Traceback (most recent call last):

File "sqrt.py", line 3, in

x = float(input("Ingresa x: "))

ValueError: could not convert string to float: 'Abracadabra'

O algo como esto:

Ingresa x: -1

Traceback (most recent call last):

File "sqrt.py", line 4, in

y = math.sqrt(x)

ValueError: math domain error

¿Puedes protegerte de tales sorpresas? Por supuesto que si. Además, tienes que
hacerlo para ser considerado un buen programador.

Excepciones
Cada vez que tu código intenta hacer algo erroneo, irresponsable o inaplicable, Python hace dos
cosas:

 Detiene tu programa.
 Crea un tipo especial de dato, llamado excepción.

Ambas actividades llevan por nombre lanzar una excepción. Podemos decir que Python siempre
lanza una excepción (o que una excepción ha sido lanzada) cuando no tiene idea de qué hacer con
el código.

¿Qué ocurre después?

 La excepción lanzada espera que alguien o algo lo note y haga algo al respecto.
 Si la excepción no es resuelta, el programa será terminado abruptamente, y verás
un mensaje de error enviado a la consola por Python.
 De otra manera, si se atiende la excepción y es manejada apropiadamente, el programa
puede reanudarse y su ejecución puede continuar.
Python proporciona herramientas efectivas que permiten observar, identificar y manejar las
excepciones eficientemente. Esto es posible debido a que todas las excepciones potenciales tienen
un nombre específico, por lo que se pueden clasificar y reaccionar a ellas adecuadamente.

Ya conoces algunos nombres de excepción.

Observa el siguiente mensaje de diagnóstico:

ValueError: math domain error

La palabra en rojo es solo el nombre de la excepción. Vamos a familiarizarnos con algunas otras
excepciones.

Excepciones: continuación
Observa el código en el editor. Ejecuta el (obviamente incorrecto) programa.

valor = 1

valor /= 0

Verás el siguiente mensaje en respuesta:

Traceback (most recent call last):

File "div.py", line 2, in

valor /= 0
ZeroDivisionError: division by zero

Este error de excepción se llama ZeroDivisionError.

Excepciones: continuación
Observa el código en el editor. ¿Qué pasará cuando lo ejecutes?

lista = []

x = lista[0]

Verás el siguiente mensaje en respuesta:

Traceback (most recent call last):

File "lst.py", line 2, in

x = lista[0]

IndexError: list index out of range

Este es el IndexError (error de índice).

Excepciones: continuación
¿Cómo se manejan las excepciones? La palabra  try  es clave para la solución.

Además, también es una palabra reservada.

La receta para el éxito es la siguiente:

 Primero, se debe intentar (try) hacer algo.


 Después, tienes que comprobar si todo salió bien.

Pero, ¿no sería mejor verificar primero todas las circunstancias y luego hacer algo solo
si es seguro?

Justo como el ejemplo en el editor.

primerNumero = int(input("Ingresa el primer numero: "))

segundoNumero = int(input("Ingresa el segundo numero: "))

if segundoNumero != 0:

print(primerNumero / segundoNumero)
else:

print("Esta operacion no puede ser realizada.")

print("FIN.")

Es cierto que esta forma puede parecer la más natural y comprensible, pero en
realidad, este método no facilita la programación. Todas estas revisiones pueden
hacer el código demasiado grande e ilegible.

Python prefiere un enfoque completamente diferente.

Excepciones: continuación
Observa el código en el editor. Este es el enfoque favorito de Python.

Nota:

 La palabra reservada  try  comienza con un bloque de código el cual puede o


no estar funcionando correctamente.
 Después, Python intenta realizar la acción arriesgada: si falla, se genera una
excepción y Python comienza a buscar una solución.
 La palabra reservada  except  comienza con un bloque de código que
será ejecutado si algo dentro del bloque  try  sale mal - si se genera una
excepción dentro del bloque anterior  try , fallará aquí, entonces el código
ubicado después de la palabra clave except debería proporcionar una reacción
adecuada a la excepción planteada.
 Se regresa al nivel de anidación anterior, es decir, se termina la sección try-
except.

Ejecute el código y prueba su comportamiento.

primerNumero = int(input("Ingresa el primer numero: "))

segundoNumero = int(input("Ingresa el segundo numero: "))

if segundoNumero != 0:

print(primerNumero / segundoNumero)

else:

print("Esta operacion no puede ser realizada.")

print("FIN.")
Resumamos esto:

try:
:
:
except:
:
:

 En el primer paso, Python intenta realizar todas las instrucciones colocadas


entre las instrucciones  try:  y  except: .
 Si no hay ningún problema con la ejecución y todas las instrucciones se realizan
con éxito, la ejecución salta al punto después de la última línea del
bloque  except:  , y la ejecución del bloque se considera completa.
 Si algo sale mal dentro del bloque  try:  o  except: , la ejecución salta
inmediatamente fuera del bloque y entra en la primera instrucción ubicada
después de la palabra reservada  except:  : esto significa que algunas de las
instrucciones del bloque pueden ser silenciosamente omitidas.

Excepciones: continuación
Observa el código en el editor. Te ayudará a comprender este mecanismo.

try:

print("1")

x=1/0

print("2")

except:

print("Oh cielos, algo salio mal...")

print("3")

Esta es la salida que produce:

Oh cielos, algo salio mal...

Nota: la instrucción  print("2")  se perdió en el proceso.


Excepciones: continuación
Este enfoque tiene una desventaja importante: si existe la posibilidad de que más de
una excepción se salte a un apartado  except: , puedes tener problemas para
descubrir lo que realmente sucedió.

Al igual que en el código en el editor. Ejecútalo y ve lo qué pasa.

try:

x = int(input("Ingresa un numero: "))

y=1/x

except:

print("Oh cielos, algo salio mal...")

print("FIN.")

El mensaje:  Oh cielos, algo salio mal...  que aparece en la consola no dice nada
acerca de la razón, mientras que hay dos posibles causas de la excepción:

 Datos no enteros fueron ingresados por el usuario.


 Un valor entero igual a  0  fue asignado a la variable  x .

Técnicamente, hay dos formas de resolver el problema:

 Construir dos bloques consecutivos try-except, uno por cada posible motivo


de excepción (fácil, pero provocará un crecimiento desfavorable del código).
 Emplear una variante más avanzada de la instrucción.

Se parece a esto:

try:
:
except exc1:
:
except exc2:
:
except:
:

Así es como funciona:


 Si el  try  lanza la excepción  exc1 , esta será manejada por el bloque  except
exc1: .
 De la misma manera, si el  try  lanza la excepción  exc2 , esta será manejada por
el bloque  except exc2: .
 Si el  try  lanza cualquier otra excepción, será manejado por el bloque sin
nombre  except .

Pasemos a la siguiente parte del curso y veámoslo en acción.

Excepciones: continuación
Mira el código en el editor. Nuestra solucion esta ahí.

try:

x = int(input("Ingresa un numero: "))

y=1/x

print(y)

except ZeroDivisionError:

print("No puedes dividir entre cero, lo siento.")

except ValueError:

print("Debes ingresar un valor entero.")

except:

print("Oh cielos, algo salio mal...")

print("THE END.")

El código, cuando se ejecute, producirá una de las siguientes cuatro variantes de


salida:

 Si se ingresa un valor entero válido distinto de cero (por ejemplo,  5 ) dirá:

0.2
 FIN.
 Si se ingresa  0 , dirá:

No puedes dividir entre cero, lo siento.


 FIN.
 Si se ingresa cualquier cadena no entera, verás:

Debes ingresar un valor entero.


 FIN.
 (Localmente en tu máquina) si presionas Ctrl-C mientras el programa está
esperando la entrada del usuario (provocará una excepción
denominada KeyboardInterrupt), el programa dirá:

Oh cielos, algo salio mal...


 FIN.

Excepciones: continuación
No olvides que:

 Los bloques  except  son analizados en el mismo orden en que aparecen en el


código.
 No debes usar más de un bloque de excepción con el mismo nombre.
 El número de diferentes bloques  except  es arbitrario, la única condición es
que si se emplea el  try , debes poner al menos un  except  (nombrado o no)
después de el.
 La palabra reservada  except  no debe ser empleada sin que le preceda un  try .
 Si uno de los bloques  except  es ejecutado, ningún otro lo será.
 Si ninguno de los bloques  except  especificados coincide con la excepción
planteada, la excepción permanece sin manejar (lo discutiremos pronto).
 Si un  except  sin nombre existe, tiene que especificarse como el último.
try:
:
except exc1:
:
except exc2:
:
except:
:

Continuemos ahora con los experimentos.

Observa el código en el editor. Hemos modificado el programa anterior, hemos


eliminado el bloque  ZeroDivisionError .

try:

x = int(input("Ingresa un numero: "))

y=1/x
print(y)

except ValueError:

print("Debes ingresar un valor entero.")

except:

print("Oh cielos, algo salio mal...")

print("FIN.")

¿Qué sucede ahora si el usuario ingresa un  0  como entrada?

Como no existe un bloque declarado para la división entre cero, la excepción cae


dentro del bloque general (sin nombre): esto significa que en este caso, el programa
dirá:

Oh cielos, algo salio mal...


FIN.

Inténtalo tú mismo. Ejecuta el programa.

Excepciones: continuación
Echemos a perder el código una vez más.

Observa el programa en el editor. Esta vez, hemos eliminado el bloque sin nombre.

try:

x = int(input("Ingresa un numero: "))

y=1/x

print(y)

except ValueError:

print("Debes ingresar un valor entero.")

print("FIN.")

El usuario ingresa nuevamente un  0 , y:

 La excepción no será manejada por  ValueError  - no tiene nada que ver con
ello.
 Como no hay otro bloque, deberías ver este mensaje:

Traceback (most recent call last):


 File "exc.py", line 3, in
 y = 1 / x
 ZeroDivisionError: division by zero

Has aprendido mucho sobre el manejo de excepciones en Python. En la siguiente


sección, nos centraremos en las excepciones integradas de Python y sus jerarquías.

 5.1.5.1 La anatomía de las excepciones

Excepciones
Python 3 define 63 excepciones integradas, y todos ellos forman una jerarquía en forma de árbol,
aunque el árbol es un poco extraño ya que su raíz se encuentra en la parte superior.

Algunas de las excepciones integradas son más generales (incluyen otras excepciones) mientras que
otras son completamente concretas (solo se representan a sí mismas). Podemos decir que cuanto
más cerca de la raíz se encuentra una excepción, más general (abstracta) es. A su vez, las
excepciones ubicadas en los extremos del árbol (podemos llamarlas hojas) son concretas.

Echa un vistazo a la figura:

Muestra una pequeña sección del árbol completo de excepciones. Comencemos examinando el árbol
desde la hoja ZeroDivisionError.

Nota:

 ZeroDivisionError es un caso especial de una clase de excepción más general


llamada ArithmeticError.
 ArithmeticError es un caso especial de una clase de excepción más general llamada
solo Exception.
 Exception es un caso especial de una clase más general llamada BaseException.
Podemos describirlo de la siguiente manera (observa la dirección de las flechas; siempre apuntan a la
entidad más general):

BaseException

Exception

ArithmeticError

ZeroDivisionError

Te mostraremos cómo funciona esta generalización. Comencemos con un código realmente simple.

Excepciones: continuación
Observa el código en el editor. Es un ejemplo simple para comenzar. Ejecutalo.

try:

y=1/0

except ZeroDivisionError:

print("Uuuppsss...")

print("FIN.")

La salida que esperamos ver se ve así:

Uuuppsss...
FIN.

Ahora observa el código a continuación:

try:
y = 1 / 0
except ArithmeticError:
print("Uuuppsss...")

print("FIN.")

Algo ha cambiado: hemos reemplazado  ZeroDivisionError  con  ArithmeticError .

Ya se sabe que  ArithmeticError  es una clase general que incluye (entre otras) la
excepción  ZeroDivisionError .
Por lo tanto, la salida del código permanece sin cambios. Pruébalo.

Esto también significa que reemplazar el nombre de la excepción ya sea


con  Exception  o  BaseException  no cambiará el comportamiento del programa.

Vamos a resumir:

 Cada excepción cae en la primer coincidencia.


 La coincidencia correspondiente no tiene que especificar exactamente la
misma excepción, es suficiente que la excepción sea mas general (mas
abstracta) que la lanzada.

Excepciones: continuación
Mira el código en el editor. ¿Qué pasará aquí?

try:

y=1/0

except ZeroDivisionError:

print("¡División entre Cero!")

except ArithmeticError:

print("¡Problema aritmético!")

print("FIN.")

La primera coincidencia es la que contiene  ZeroDivisionError . Significa que la


consola mostrará:

¡División entre Cero!


FIN.

¿Cambiará algo si intercambiamos los dos  except ? Justo como aquí abajo:

try:
y = 1 / 0
except ArithmeticError:
print("¡Problema aritmético!")
except ZeroDivisionError:
print("¡División entre Cero!")
print("FIN.")

El cambio es radical: la salida del código es ahora:

¡Problema aritmético!
FIN.

¿Por qué, si la excepción planteada es la misma que antes?

La excepción es la misma, pero la excepción más general ahora aparece primero:


también capturará todas las divisiones entre cero. También significa que no hay
posibilidad de que alguna excepción llegue a ZeroDivisionError. Ahora es
completamente inalcanzable.

Recuerda:

 ¡El orden de las excepciones importa!


 No pongas excepciones más generales antes que otras más concretas.
 Esto hará que el último sea inalcanzable e inútil.
 Además, hará que el código sea desordenado e inconsistente.
 Python no generará ningún mensaje de error con respecto a este problema.

Excepciones: continuación
Si deseas manejar dos o mas excepciones de la misma manera, puedes usar la
siguiente sintaxis:

try:
:
except (exc1, exc2):
:

Simplemente tienes que poner todos los nombres de excepción empleados en una
lista separada por comas y no olvidar los paréntesis.

Si una excepción se genera dentro de una función, puede ser manejada:

 Dentro de la función.
 Fuera de la función.

Comencemos con la primera variante: observa el código en el editor.

def badFun(n):
try:

return 1 / n

except ArithmeticError:

print("¡Problema aritmético!")

return None

badFun(0)

print("FIN.")

La excepción ZeroDivisionError (la cual es un caso concreto de la


clase ArithmeticError) es lanzada dentro de la función  badfun() , y la función en sí
misma se encarga de su caso.

La salida del programa es:

¡Problema aritmético!
FIN.

También es posible dejar que la excepción se propague fuera de la función.


Probémoslo ahora.

Observa el código a continuación:

def badFun(n):
return 1 / n

try:
badFun(0)
except ArithmeticError:
print("¿Que pasó? ¡Se lanzo una excepción!")

print("FIN.")

El problema tiene que ser resuelto por el invocador (o por el invocador del invocador, y
así sucesivamente.).

La salida del programa es:

¿Que pasó? ¡Se lanzo una excepción!


FIN.
Nota: la excepción planteada puede cruzar la función y los límites del módulo, y
viajar a través de la cadena de invocación buscando una cláusula  except  capaz de
manejarla.

Si no existe tal cláusula, la excepción no se controla y Python resuelve el problema de


la manera estándar - terminando el código y emitiendo un mensaje de
diagnóstico.

Ahora vamos a suspender esta discusión, ya que queremos presentarte una nueva
instrucción de Python.

Excepciones: continuación
La instrucción  raise  genera la excepción especificada denominada  exc  como si fuese
generada de manera natural:

raise exc

Nota:  raise  es una palabra reservada.

La instrucción permite:

 Simular excepciones reales (por ejemplo, para probar tu estrategia de


manejo de excepciones).
 Parcialmente manejar una excepción y hacer que otra parte del código sea
responsable de completar el manejo.

Observa el código en el editor. Así es como puedes usarlo en la práctica.

def badFun(n):

raise ZeroDivisionError

try:

badFun(0)

except ArithmeticError:

print("¿Que pasó? ¿Un error?")

print("FIN.")

La salida del programa permanece sin cambios.

De esta manera, puedes probar tu rutina de manejo de excepciones sin forzar al


código a hacer cosas incorrectas.
Excepciones: continuación
La instrucción  raise  también se puede utilizar de la siguiente manera (toma en
cuenta la ausencia del nombre de la excepción):

raise

Existe una seria restricción: esta variante de la instrucción  raise  puede ser
utilizada solamente dentro de la rama  except ; usarla en cualquier otro contexto
causa un error.

La instrucción volverá a generar la misma excepción que se maneja actualmente.

Gracias a esto, puedes distribuir el manejo de excepciones entre diferentes partes del
código.

Observa el código en el editor. Ejecútalo, lo veremos en acción.

def badFun(n):

try:

return n / 0

except:

print("¡Lo hice otra vez!")

raise

try:

badFun(0)

except ArithmeticError:

print("¡Ya veo!")

print("FIN.")

La excepción ZeroDivisionError es generada dos veces:

 Primero, dentro del  try  debido a que se intentó realizar una división entre
cero.
 Segundo, dentro de la parte  except  por la instrucción  raise .

En efecto, la salida del código es:

¡Lo hice otra vez!


¡Ya veo!

FIN.

Excepciones: continuación
Ahora es un buen momento para mostrarte otra instrucción de Python,
llamada  assert  (afirmar). Esta es una palabra reservada.

assert expresión

¿Como funciona?

 Evalúa la expresión.
 Si la expresión se evalúa como  True (verdadero) , o un valor numérico
distinto de cero, o una cadena no vacía, o cualquier otro valor diferente
de  None , no hará nada más.
 De lo contrario, automáticamente e inmediatamente genera una excepción
llamada AssertionError (en este caso, decimos que la afirmación ha fallado).

¿Cómo puede ser utilizada?

 Puedes ponerlo en la parte del código donde quieras estar absolutamente a


salvo de datos incorrectos, y donde no estés absolutamente seguro de que
los datos hayan sido examinados cuidadosamente antes (por ejemplo, dentro
de una función utilizada por otra persona).
 El generar una excepción AssertionError asegura que tu código no
produzca resultados no válidos y muestra claramente la naturaleza de la falla.
 Las aserciones no reemplazan las excepciones ni validan los datos, son
suplementos.

Si las excepciones y la validación de datos son como conducir con cuidado, la aserción
puede desempeñar el papel de una bolsa de aire.

Veamos a la instrucción  assert  en acción. Mira el código en el editor. Ejecutarlo.

import math
x = float(input("Ingresa un numero: "))

assert x >= 0.0

x = math.sqrt(x)

print(x)

El programa se ejecuta sin problemas si se ingresa un valor numérico válido mayor o


igual a cero; de lo contrario, se detiene y emite el siguiente mensaje:

Traceback (most recent call last):


File ".main.py", line 4, in
assert x >= 0.0

AssertionError

Excepciones integradas
Te mostraremos una breve lista de las excepciones más útiles. Si bien puede sonar extraño llamar
"útil" a una cosa o un fenómeno que es un signo visible de una falla o retroceso, como sabes, errar es
humano y si algo puede salir mal, saldrá mal.

Las excepciones son tan rutinarias y normales como cualquier otro aspecto de la vida de un
programador.

Para cada excepción, te mostraremos:

 Su nombre.
 Su ubicación en el árbol de excepciones.
 Una breve descripción.
 Un fragmento de código conciso que muestre las circunstancias en las que se puede generar
la excepción.

Hay muchas otras excepciones para explorar: simplemente no tenemos el espacio para revisarlas
todas aquí.

ArithmeticError
Ubicación:

BaseException ← Exception ← ArithmeticError

Descripción:

Una excepción abstracta que incluye todas las excepciones causadas por operaciones aritméticas
como división cero o dominio inválido de un argumento.

AssertionError
Ubicación:
BaseException ← Exception ← AssertionError

Descripción:

Una excepción concreta generada por la instrucción de aserción cuando su argumento se evalúa
como  False  (falso),  None  (ninguno),  0 , o una cadena vacía.

Código :

from math import tan, radians

angle = int(input('Ingresa el angulo entero en grados: '))

# debemos estar seguros de ese angulo != 90 + k * 180

assert angle % 180 != 90

print(tan(radians(angle)))

BaseException
Ubicación:

BaseException

Descripción:

La excepción más general (abstracta) de todas las excepciones de Python: todas las demás
excepciones se incluyen en esta; se puede decir que las siguientes dos excepciones son
equivalentes:  except:  y  except BaseException: .

IndexError
Ubicación:

BaseException ← Exception ← LookupError ← IndexError

Descripción:

Una excepción concreta que surge cuando se intenta acceder al elemento de una secuencia
inexistente (por ejemplo, el elemento de una lista).

Código:

# el codigo muestra una forma extravagante

# de dejar el bucle

lista = [1, 2, 3, 4, 5]
ix = 0

doit = True

while doit:

try:

print(lista[ix])

ix += 1

except IndexError:

doit = False

print('Listo')

KeyboardInterrupt
Ubicación:

BaseException ← KeyboardInterrupt

Descripción:

Una excepción concreta que surge cuando el usuario usa un atajo de teclado diseñado para terminar
la ejecución de un programa (Ctrl-C en la mayoría de los Sistemas Operativos); si manejar esta
excepción no conduce a la terminación del programa, el programa continúa su ejecución. Nota: esta
excepción no se deriva de la clase Exception. Ejecuta el programa en IDLE.

Código:

# este código no puede ser terminado

# presionando Ctrl-C

from time import sleep

seconds = 0

while True:

try:

print(seconds)

seconds += 1

sleep(1)

except KeyboardInterrupt:
print("¡No hagas eso!")

LookupError
Ubicación:

BaseException ← Exception ← LookupError

Descripción:

Una excepción abstracta que incluye todas las excepciones causadas por errores resultantes de
referencias no válidas a diferentes colecciones (listas, diccionarios, tuplas, etc.).

MemoryError
Ubicación:

BaseException ← Exception ← MemoryError

Descripción:

Se produce una excepción concreta cuando no se puede completar una operación debido a la falta de
memoria libre.

Código:

# este código causa la excepción MemoryError

# advertencia: ejecutar este código puede ser crucial

# para tu sistema operativo

# ¡no lo ejecutes en entornos de producción!

string = 'x'

try:

while True:

string = string + string

print(len(string))

except MemoryError:

print('¡Esto no es gracioso!')
OverflowError
Ubicación:

BaseException ← Exception ← ArithmeticError ← OverflowError

Descripción:

Una excepción concreta que surge cuando una operación produce un número demasiado grande para
ser almacenado con éxito.

Código:

# el código imprime los valores subsequentes

# de exp(k), k = 1, 2, 4, 8, 16, ...

from math import exp

ex = 1

try:

while True:

print(exp(ex))

ex *= 2

except OverflowError:

print('El número es demasiado grande.')

ImportError
Ubicación:

BaseException ← Exception ← StandardError ← ImportError

Descripción:

Se produce una excepción concreta cuando falla una operación de importación.

Código:

# una de estas importaciones fallará, ¿cuál será?

try:

import math

import time
import abracadabra

except:

print('Una de sus importaciones ha fallado.')

KeyError
Ubicación:

BaseException ← Exception ← LookupError ← KeyError

Descripción:

Una excepción concreta que surge cuando intentas acceder al elemento inexistente de una colección
(por ejemplo, el elemento de un diccionario).

Código:

# como abusar del diccionario

# y cómo lidiar con ello

dict = { 'a' : 'b', 'b' : 'c', 'c' : 'd' }

ch = 'a'

try:

while True:

ch = dict[ch]

print(ch)

except KeyError:

print('No existe tal clave:', ch)

Hemos terminado con excepciones por ahora, pero volverán cuando discutamos la programación
orientada a objetos en Python. Puedes usarlos para proteger tu código de accidentes graves, pero
también tienes que aprender a sumergirte en ellos, explorando la información que llevan.

De hecho, las excepciones son objetos; sin embargo, no podemos decirle nada sobre este aspecto
hasta que te presentemos clases, objetos y similares.

Por el momento, si deseas obtener más información sobre las excepciones por tu cuenta, consulta la
Biblioteca estándar de Python en https://docs.python.org/3.6/library/exceptions.html.
 5.1.6.4 Leer ints de forma segura

LABORATORIO

Tiempo Estimado
15-25 minutos

Nivel de dificultad
Mediano

Objetivos
 Mejorar las habilidades del alumno para definir funciones.
 Utilizar excepciones para proporcionar un entorno de entrada seguro.

Escenario
Tu tarea es escribir una función capaz de ingresar valores enteros y verificar si
están dentro de un rango especificado.

La función debe:

 Aceptar tres argumentos: una entrada, un límite inferior aceptable y un límite


superior aceptable.
 Si el usuario ingresa una cadena que no es un valor entero, la función debe
emitir el mensaje  Error: entrada incorrecta , y solicitará al usuario que
ingrese el valor nuevamente.
 Si el usuario ingresa un número que está fuera del rango especificado, la
función debe emitir el mensaje  Error: el valor no está dentro del
rango permitido (min..max)  y solicitará al usuario que ingrese el valor
nuevamente.
 Si el valor de entrada es válido, será regresado como resultado.

Datos de Prueba
Prueba tu código cuidadosamente.

Así es como la función debería reaccionar ante la entrada del usuario:

Ingresa un número entre -10 a 10: 100

Error: el valor no está dentro del rango permitido (-10..10)

Ingresa un número entre -10 a 10: asd


Error: entrada incorrecta

Ingresa un número entre -10 a 10: 1

El número es: 1

def readint(prompt, min, max):


ok=False
while not ok:
try:
valor = int(input(prompt))
ok = True
except ValorError:
print('Error: entrada incorrecta')
if ok:
ok = valor >= min and valor <=max
if not ok:
print("Error: el valor no ésta dentro del rangp permitido (" +
str(min) + ".." + str(max) + ")")
return valor;

v = readint("Ingresa un numero de -10 a 10: ", -10, 10)

print("El numero es:", v)

 5.1.7.1 Caracteres y Cadenas

Cómo las computadoras entienden los caracteres


individuales
Has escrito algunos programas interesantes desde que comenzó este curso, pero todos ellos han
procesado solo un tipo de datos: los números. Como sabes (puedes ver esto en todas partes), muchos
datos de la computadora no son números: nombres, apellidos, direcciones, títulos, poemas,
documentos científicos, correos electrónicos, sentencias judiciales, confesiones de amor y mucho,
mucho más.
Todos estos datos deben ser almacenados, ingresados, emitidos, buscados y transformados por
computadoras como cualquier otro dato, sin importar si son caracteres únicos o enciclopedias de
múltiples volúmenes.

¿Como es posible?

¿Cómo puedes hacerlo en Python? Esto es lo que discutiremos ahora. Comencemos con cómo las
computadoras entienden los caracteres individuales.

Las computadoras almacenan los caracteres como números. Cada carácter utilizado por una
computadora corresponde a un número único, y viceversa. Esta asignación debe incluir más
caracteres de los que podrías esperar. Muchos de ellos son invisibles para los humanos, pero
esenciales para las computadoras.

Algunos de estos caracteres se llaman espacios en blanco, mientras que otros se


nombran caracteres de control, porque su propósito es controlar dispositivos de entrada / salida.

Un ejemplo de un espacio en blanco que es completamente invisible a simple vista es un código


especial, o un par de códigos (diferentes sistemas operativos pueden tratar este asunto de manera
diferente), que se utilizan para marcar el final de las líneas dentro de los archivos de texto.

Las personas no ven este signo (o estos signos), pero pueden observar el efecto de su aplicación
donde ven un salto de línea.

Podemos crear prácticamente cualquier cantidad de asignaciones de números con caracteres, pero la
vida en un mundo en el que cada tipo de computadora utiliza una codificación de caracteres
diferentes no sería muy conveniente. Este sistema ha llevado a la necesidad de introducir un estándar
universal y ampliamente aceptado, implementado por (casi) todas las computadoras y sistemas
operativos en todo el mundo.

El denominado ASCII (por sus siglas en íngles American Standard Code for Information


Interchange). El Código Estándar Americano para Intercambio de Información es el más utilizado,
y es posible suponer que casi todos los dispositivos modernos (como computadoras, impresoras,
teléfonos móviles, tabletas, etc.) usan este código.

El código proporciona espacio para 256 caracteres diferentes, pero solo nos interesan los primeros
128. Si deseas ver cómo se construye el código, mira la tabla a continuación. Haz clic en la tabla
para ampliarla. Mírala cuidadosamente: hay algunos datos interesantes. Observa el código del
caracter más común: el espacio. El cual es el 32.
Ahora verifica el código de la letra minúscula a. El cual es 97. Ahora encuentra la A mayúscula. Su
codigo es 65. Ahora calcula la diferencia entre el código de la a y la A. Es igual a 32. Ese es el códgo
del espacio. Interesante, ¿no es así?

También ten en cuenta que las letras están ordenadas en el mismo orden que en el alfabeto latino.

I18N
Ahora, el alfabeto latino no es suficiente para toda la humanidad. Los usuarios de ese alfabeto son
minoría. Era necesario idear algo más flexible y capaz que ASCII, algo capaz de hacer que todo el
software del mundo sea susceptible de internacionalización, porque diferentes idiomas usan
alfabetos completamente diferentes, y a veces estos alfabetos no son tan simples como el latino.

La palabra internacionalización se acorta comúnmente a I18N.


¿Por qué? Observa con cuidado, hay una I al inicio de la palabra, a continuación hay 18 letras
diferentes, y una N al final.

A pesar del origen ligeramente humorístico, el término se utiliza oficialmente en muchos


documentos y normas.

El software I18N es un estándar en los tiempos actuales. Cada programa tiene que ser escrito de una
manera que permita su uso en todo el mundo, entre diferentes culturas, idiomas y alfabetos.

El código ASCII emplea ocho bits para cada signo. Ocho bits significan 256 caracteres diferentes.
Los primeros 128 se usan para el alfabeto latino estándar (tanto en mayúsculas como en minúsculas).
¿Es posible colocar todos los otros caracteres utilizados en todo el mundo a los 128 lugares
restantes?

No, no lo es.

Puntos de código y páginas de códigos


Necesitamos un nuevo término: un punto de código.

Un punto de código es un numero que compone un caracter. Por ejemplo, 32 es un punto de


código que compone un espacio en codificación ASCII. Podemos decir que el código ASCII
estándar consta de 128 puntos de código.

Como el ASCII estándar ocupa 128 de 256 puntos de código posibles, solo puedes hacer uso de los
128 restantes.

No es suficiente para todos los idiomas posibles, pero puede ser suficiente para un idioma o para un
pequeño grupo de idiomas similares.

¿Se puede establecer la mitad superior de los puntos de código de manera diferente para
diferentes idiomas? Si, por supuesto. A tal concepto se le denomina una página de códigos.

Una página de códigos es un estándar para usar los 128 puntos de código superiores para
almacenar caracteres específicos. Por ejemplo, hay diferentes páginas de códigos para Europa
Occidental y Europa del Este, alfabetos cirílicos y griegos, idiomas árabe y hebreo, etc.

Esto significa que el mismo punto de código puede formar diferentes caracteres cuando se usa en
diferentes páginas de códigos.

Por ejemplo, el punto de código 200 forma una Č (una letra usada por algunas lenguas eslavas)
cuando lo utiliza la página de códigos ISO/IEC 8859-2, pero forma un Ш (una letra cirílica) cuando
es usado por la página de códigos ISO/IEC 8859-5.
En consecuencia, para determinar el significado de un punto de código específico, debes conocer la
página de códigos de destino.

En otras palabras, los puntos de código derivados del código de páginas son ambiguos

Unicode
Las páginas de códigos ayudaron a la industria informática a resolver problemas de I18N durante
algún tiempo, pero pronto resultó que no serían una solución permanente.

El concepto que resolvió el problema a largo plazo fue el Unicode.

Unicode asigna caracteres únicos (letras, guiones, ideogramas, etc.) a más de un millón de
puntos de código. Los primeros 128 puntos de código Unicode son idénticos a ASCII, y los
primeros 256 puntos de código Unicode son idénticos a la página de códigos ISO / IEC 8859-1 (una
página de códigos diseñada para idiomas de Europa occidental).

UCS-4
El estándar Unicode no dice nada sobre cómo codificar y almacenar los caracteres en la memoria y
los archivos. Solo nombra todos los caracteres disponibles y los asigna a planos (un grupo de
caracteres de origen, aplicación o naturaleza similares).

Existe más de un estándar que describe las técnicas utilizadas para implementar Unicode en
computadoras y sistemas de almacenamiento informáticos reales. El más general de ellos es UCS-4.

El nombre viene de Universal Character Set (Conjunto de Caracteres Universales).


UCS-4 emplea 32 bits (cuatro bytes) para almacenar cada caracter, y el código es solo el
número único de los puntos de código Unicode. Un archivo que contiene texto codificado UCS-4
puede comenzar con un BOM (byte order mark - marca de orden de bytes), una combinación no
imprimible de bits que anuncia la naturaleza del contenido del archivo. Algunas utilidades pueden
requerirlo.

Como puedes ver, UCS-4 es un estándar bastante derrochador: aumenta el tamaño de un texto cuatro
veces en comparación con el estándar ASCII. Afortunadamente, hay formas más inteligentes de
codificar textos Unicode.

UTF-8
Uno de los más utilizados es UTF-8.

El nombre se deriva de Unicode Transformation Format (Formato de Transformación Unicode).

El concepto es muy inteligente. UTF-8 emplea tantos bits para cada uno de los puntos de código
como realmente necesita para representarlos.

Por ejemplo:

 Todos los caracteres latinos (y todos los caracteres ASCII estándar) ocupan ocho bits.
 Los caracteres no latinos ocupan 16 bits.
 Los ideógrafos CJK (China-Japón-Corea) ocupan 24 bits.

Debido a las características del método utilizado por UTF-8 para almacenar los puntos de código, no
es necesario usar el BOM, pero algunas de las herramientas lo buscan al leer el archivo, y muchos
editores lo configuran durante el guardado.

Python 3 es totalmente compatible con Unicode y UTF-8:

 Puedes usar caracteres codificados Unicode / UTF-8 para nombrar variables y otras
entidades.
 Puedes usarlos durante todas las entradas y salidas.

Esto significa que Python3 está completamente Internacionalizado.


Cadenas - una breve reseña
Hagamos un breve repaso de la naturaleza de las cadenas en Python.

En primer lugar, las cadenas de Python (o simplemente cadenas, ya que no vamos a


discutir las cadenas de ningún otro lenguaje) son secuencias inmutables.

Es muy importante tener en cuenta esto, porque significa que debes esperar un
comportamiento familiar.

Por ejemplo, la función  len()  empleada por cadenas devuelve el número de


caracteres que contiene el argumento.

Observa el Ejemplo 1 en el editor. La salida del código es  3 .

# Ejemplo 1

palabra = 'por'

print(len(palabra))

Cualquier cadena puede estar vacía. Si es el caso, su longitud es  0  como en el Ejemplo
2.

# Ejemplo 2

vacio = ''

print(len(vacio))

No olvides que la diagonal invertida ( \ ) empleada como un caracter de escape, no esta


incluida en la longitud total de la cadena.

El código en el Ejemplo 3, da como salida un  3 .

# Ejemplo 3

yo_soy = 'I\'m'

print(len(yo_soy))

Ejecuta los tres ejemplos de código y verificalo.


Cadenas multilínea
Ahora es un muy buen momento para mostrarte otra forma de especificar cadenas
dentro del código fuente de Python. Ten en cuenta que la sintaxis que ya conoces no
te permitirá usar una cadena que ocupe más de una línea de texto.

Por esta razón, el código aquí es erróneo:

multiLinea = 'Linea #1

Linea #2'

print(len(multiLinea))

Afortunadamente, para este tipo de cadenas, Python ofrece una sintaxis simple,
conveniente y separada.

Observa el código en el editor. Así es comos se ve.

multiLinea = '''Linea #1

Linea #2'''

print(len(multiLinea))

Como puedes ver, la cadena comienza con tres apóstrofes, no uno. El mismo


apóstrofe triplicado se usa para terminar la cadena.

El número de líneas de texto dentro de una cadena de este tipo es arbitrario.

La salida del código es  17 .

Cuenta los caracteres con cuidado. ¿Es este resultado correcto o no? Se ve bien a
primera vista, pero cuando cuentas los caracteres, no lo es.

La Linea #1  contiene ocho caracteres. Las dos líneas juntas contienen 16 caracteres.
¿Perdimos un caracter? ¿Dónde? ¿Cómo?

No, no lo hicimos.

El caracter que falta es simplemente invisible: es un espacio en blanco. Se


encuentra entre las dos líneas de texto.
Se denota como:  \n .

¿Lo recuerdas? Es un caracter especial (de control) utilizado para forzar un avance de


línea. No puedes verlo, pero cuenta.

Las cadenas multilínea pueden ser delimitadas también por comillas triples, como
aqui:

multiLinea = """Linea #1

Linea #2"""

print(len(multiLinea))

Elije el método que sea más cómodo. Ambos funcionan igual.

Operaciones con Cadenas


Al igual que otros tipos de datos, las cadenas tienen su propio conjunto de
operaciones permitidas, aunque son bastante limitadas en comparación con los
números.

En general, las cadenas pueden ser:

 Concatenadas (unidas).
 Replicadas.

La primera operación la realiza el operador  +  (toma en cuenta que no es una adición o


suma) mientras que la segunda por el operador  *  (toma en cuenta de nuevo que no es
una multiplicación).

La capacidad de usar el mismo operador en tipos de datos completamente diferentes


(como números o cadenas) se llama overloading - sobrecarga (debido a que el
operador está sobrecargado con diferentes tareas).

Analiza el ejemplo:

str1 = 'a'

str2 = 'b'

print(str1 + str2)

print(str2 + str1)

print(5 * 'a')
print('b' * 4)

 El operador  +  es empleado en dos o más cadenas y produce una nueva cadena
que contiene todos los caracteres de sus argumentos (nota: el orden es
relevante aquí, en contraste con su versión numérica, la cual es conmutativa).
 El operador  *  necesita una cadena y un número como argumentos; en este
caso, el orden no importa: puedes poner el número antes de la cadena, o
viceversa, el resultado será el mismo: una nueva cadena creada por la enésima
replicación de la cadena del argumento.

El fragmento de código produce el siguiente resultado:

ab
ba
aaaaa
bbbb

Nota: Los atajos de los operadores anteriores también son aplicables para las cadenas
( +=  y  *= ).

Operaciones con cadenas: ord()


Si deseas saber el valor del punto de código ASCII/UNICODE de un caracter
específico, puedes usar la función  ord()  (proveniente de ordinal).

La función necesita una cadena de un caracter como argumento - incumplir este


requisito provoca una excepción TypeError, y devuelve un número que representa el
punto de código del argumento.

Observa el código en el editor y ejecútalo. Las salida del fragmento de código es:

# Demostrando la función ord ()

ch1 = 'a'

ch2 = ' ' # espacio

print(ord(ch1))

print(ord(ch2))

97

32

Ahora asigna diferentes valores a  ch1  y  ch2 , por ejemplo,  α  (letra griega alfa), y  ę  (una
letra en el alfabeto polaco); luego ejecuta el código y ve qué resultado produce. Realiza
tus propios experimentos.
Operaciones con cadenas: chr()
Si conoces el punto de código (número) y deseas obtener el carácter correspondiente,
puedes usar la función llamada  chr() .

La función toma un punto de código y devuelve su carácter.

Invocándolo con un argumento inválido (por ejemplo, un punto de código negativo o


inválido) provoca las excepciones ValueError o TypeError.

Ejecuta el código en el editor, su salida es la siguiente:

# Demostrando la función chr()

print(chr(97))

print(chr(945))

a
α

Nota:

 chr(ord(x)) == x
 ord(chr(x)) == x

De nuevo, realiza tus propios experimentos.

Cadenas como secuencias: indexación


Ya dijimos antes que las cadenas de Python son secuencias. Es hora de mostrarte lo
que significa realmente.

Las cadenas no son listas, pero pueden ser tratadas como tal en muchos casos.

Por ejemplo, si deseas acceder a cualquiera de los caracteres de una cadena, puedes
hacerlo usando indexación, al igual que en el ejemplo en el editor. Ejecuta el
programa.

# Indexando cadenas

exampleString = 'silly walks'

for ix in range(len(exampleString)):

print(exampleString[ix], end=' ')


print()

Ten cuidado, no intentes pasar los límites de la cadena, ya que provocará una
excepción.

El resultado de ejemplo es:

s i l l y w a l k s

Por cierto, los índices negativos también se comportan como se esperaba. Comprueba
esto tú mismo.

Cadenas como secuencias: iterando


Iterar a través de las cadenas funciona también. Observa el siguiente ejemplo:

# Iterando a través de una cadena

exampleString = 'silly walks'

for ch in exampleString:

print(ch, end=' ')

print()

La salida es la misma que el ejemplo anterior, revisalo.

Rodajas o Rebanadas
Todo lo que sabes sobre rodajas o rebanadas es utilizable.

Hemos reunido algunos ejemplos que muestran cómo funcionan las rodajas en el
mundo de las cadenas. Mira el código en el editor, analizalo y ejecútalo.

# Rodajas o rebanadas

alpha = "abdefg"

print(alpha[1:3])

print(alpha[3:])

print(alpha[:3])

print(alpha[3:-2])
print(alpha[-3:4])

print(alpha[::2])

print(alpha[1::2])

No verás nada nuevo en el ejemplo, pero queremos que estés seguro de entender
todas las líneas del código.

La salida del código es:

bd

efg

abd

adf

beg

Ahora haz tus propios experimentos.

Los operadores in y not in
El operador  in  no debería sorprenderte cuando se aplica a cadenas,
simplemente comprueba si el argumento izquierdo (una cadena) se puede
encontrar en cualquier lugar dentro del argumento derecho (otra cadena).

El resultado es simplemente  True (Verdadero)  o  False (Falso) .

Observa el ejemplo en el editor. Así es como el operador  in  funciona.

alpfabeto = "abcdefghijklmnopqrstuvwxyz"

print("f" in alpfabeto)

print("F" in alpfabeto)

print("1" in alpfabeto)

print("ghi" in alpfabeto)

print("Xyz" in alpfabeto)

El resultado de ejemplo es:True


False

False

True

False

Como probablemente puedas deducir, el operador  not in  también es aplicable aquí.

Así es como funciona:

alfabeto = "abcdefghijklmnopqrstuvwxyz"

print("f" not in alfabeto)

print("F" not in alfabeto)

print("1" not in alfabeto)

print("ghi" not in alfabeto)

print("Xyz" not in alfabeto)

El resultado de ejemplo es:

False

True

True

False

True

Las cadenas de Python son inmutables


También te hemos dicho que las cadenas de Python son inmutables. Esta es una
característica muy importante. ¿Qué significa?

Esto significa principalmente que la similitud de cadenas y listas es limitada. No todo lo


que puede hacerse con una lista puede hacerse con una cadena.

La primera diferencia importante no te permite usar la instrucción  del  para


eliminar cualquier cosa de una cadena.

El ejemplo siguiente no funcionará:


alfabeto = "abcdefghijklmnopqrstuvwxyz"

del alfabeto[0]

Lo único que puedes hacer con  del  y una cadena es eliminar la cadena como un
todo. Intenta hacerlo.

Las cadenas de Python no tienen el método  append()  - no se pueden expander de


ninguna manera.

El siguiente ejemplo es erróneo:

alfabeto = "abcdefghijklmnopqrstuvwxyz"

alfabeto.append("A")

Con la ausencia del método  append() , el método  insert()  también es ilegal:

alfabeto = "abcdefghijklmnopqrstuvwxyz"

alfabeto.insert(0, "A")

Operaciones con cadenas: continuación


No pienses que la inmutabilidad de una cadena limita tu capacidad de operar con
ellas.

La única consecuencia es que debes recordarlo e implementar tu código de una


manera ligeramente diferente: observa el código en el editor.

Esta forma de código es totalmente aceptable, funcionará sin doblar las reglas de
Python y traerá el alfabeto latino completo a tu pantalla:

alfabeto = "bcdefghijklmnopqrstuvwxy"

alfabeto = "a" + alfabeto

alfabeto = alfabeto + "z"


print(alfabeto)

abcdefghijklmnopqrstuvwxyz

Es posible que desees preguntar si el crear una nueva copia de una cadena cada
vez que se modifica su contenido empeora la efectividad del código.

Sí lo hace. Un poco. Sin embargo, no es un problema en absoluto.

Operaciones con cadenas: min()


Ahora que comprendes que las cadenas son secuencias, podemos mostrarte algunas
capacidades de secuencia menos obvias. Las presentaremos utilizando cadenas, pero
no olvides que las listas también pueden adoptar los mismos trucos.

Comenzaremos con la función llamada  min() .

Esta función encuentra el elemento mínimo de la secuencia pasada como


argumento. Existe una condición - la secuencia (cadena o lista) no puede estar vacía,
de lo contrario obtendrás una excepción ValueError.

El programa Ejemplo 1 da la siguiente salida:

# Demonstrando min() - Ejemplo 1

print(min("aAbByYzZ"))

Nota: Es una A mayúscula. ¿Por qué? Recuerda la tabla ASCII, ¿qué letras ocupan las
primeras posiciones, mayúsculas o minúsculas?

Hemos preparado dos ejemplos más para analizar: Ejemplos 2 y 3.

# Demonstrando min() - Examplos 2 y 3

t = 'Los Caballeros Que Dicen "¡Ni!"'

print('[' + min(t) + ']')

t = [0, 1, 2]

print(min(t))
Como puedes ver, presentan más que solo cadenas. El resultado esperado se ve de la
siguiente manera:

[ ]

Nota: hemos utilizado corchetes para evitar que el espacio se pase por alto en tu
pantalla.

Operaciones con cadenas: max()


Del mismo modo, una función llamada  max()  encuentra el elemento máximo de la
secuencia.

Observa el Ejemplo 1 en el editor. La salida del programa es:

# Demostrando max() - Ejemplo 1

print(max("aAbByYzZ"))

Nota: es una z minúscula.

Ahora veamos la función  max()  a los mismos datos del ejemplo anterior. Observa
los Ejemplos 2 y 3 en el editor.

# Demonstrando max() - Examplos 2 y 3

t = 'Los Caballeros Que Dicen "¡Ni!"'

print('[' + max(t) + ']')

t = [0, 1, 2]

print(max(t))

La salida esperada es:

[¡]

2
Realiza tus propios experimentos.
Operaciones con cadenas: el método index()
El método  index()  (es un método, no una función) busca la secuencia desde el
principio, para encontrar el primer elemento del valor especificado en su
argumento.

Nota: el elemento buscado debe aparecer en la secuencia - su ausencia causará una
excepción ValueError.

El método devuelve el índice de la primera aparición del argumento (lo que significa


que el resultado más bajo posible es 0, mientras que el más alto es la longitud del
argumento decrementado por 1).

Por lo tanto, el ejemplo en la salida del editor es:

# Demonstrando el método index()

print("aAbByYzZaA".index("b"))

print("aAbByYzZaA".index("Z"))

print("aAbByYzZaA".index("A"))

Operaciones con cadenas: la función list()


La función  list()  toma su argumento (una cadena) y crea una nueva lista que
contiene todos los caracteres de la cadena, uno por elemento de la lista.

Nota: no es estrictamente una función de cadenas -  list()  es capaz de crear una
nueva lista de muchas otras entidades (por ejemplo, de tuplas y diccionarios).

Observa el código de ejemplo en el editor.

# Demostrando la función list()

print(list("abcabc"))

La salida es:

['a', 'b', 'c', 'a', 'b', 'c']


Operaciones con cadenas: el método count()
El método  count()  cuenta todas las apariciones del elemento dentro de la
secuencia. La ausencia de tal elemento no causa ningún problema.

Observa el segundo ejemplo en el editor. ¿Puedes adivinar su salida?

# Demostrando el método count()

print("abcabc".count("b"))

print('abcabc'.count("d"))

Es:

Las cadenas de Python tienen un número significativo de métodos destinados


exclusivamente al procesamiento de caracteres. No esperes que trabajen con otras
colecciones. La lista completa se presenta
aquí: https://docs.python.org/3.4/library/stdtypes.html#string-methods.

Te mostraremos los que consideramos más útiles.

El método capitalize()
Veamos algunos métodos estándar de cadenas en Python. Vamos a analizarlos en
orden alfabético, cualquier orden tiene tanto desventajas como ventajas, por lo que la
elección puede ser aleatoria.

El método  capitalize()  hace exactamente lo que dice - crea una nueva cadena
con los caracteres tomados de la cadena fuente, pero intenta modificarlos de la
siguiente manera:

 Si el primer caracter dentro de la cadena es una letra (nota: el primer


carácter es el elemento con un índice igual a 0, no es el primer caracter
visible), se convertirá a mayúsculas.
 Todas las letras restantes de la cadena se convertirán a minúsculas.

No olvides que:

 La cadena original (desde la cual se invoca el método) no se cambia de ninguna


manera (la inmutabilidad de una cadena debe obedecerse sin reservas).
 La cadena modificada (en mayúscula en este caso) se devuelve como resultado;
si no se usa de alguna manera (asígnala a una variable o pásala a una función /
método) desaparecerá sin dejar rastro.

Nota: los métodos no tienen que invocarse solo dentro de las variables. Se pueden
invocar directamente desde dentro de literales de cadena. Usaremos esa convención
regularmente: simplificará los ejemplos, ya que los aspectos más importantes no
desaparecerán entre asignaciones innecesarias.

Echa un vistazo al ejemplo en el editor. Ejecutalo.

# Demostración del método capitalize()

print('aBcD'.capitalize())

Esto es lo que imprime:

Abcd

Prueba algunos ejemplos más avanzados y prueba su salida:

print("Alpha".capitalize())
print('ALPHA'.capitalize())
print(' Alpha'.capitalize())
print('123'.capitalize())
print("αβγδ".capitalize())

El método center()
La variante de un parámetro del método  center()  genera una copia de la cadena
original, tratando de centrarla dentro de un campo de un ancho especificado.

El centrado se realiza realmente al agregar algunos espacios antes y después de la


cadena.

No esperes que este método demuestre habilidades sofisticadas. Es bastante simple.

El ejemplo en el editor usa corchetes para mostrar claramente dónde comienza y


termina realmente la cadena centrada.

# Demostración del método center()

print('[' + 'alfa'.center(10) + ']')

Su salida se ve de la siguiente manera:


[ alfa ]

Si la longitud del campo de destino es demasiado pequeña para ajustarse a la cadena,


se devuelve la cadena original.

Puedes ver el método  center()  en más ejemplos aquí:

print('[' + 'Beta'.center(2) + ']')

print('[' + 'Beta'.center(4) + ']')

print('[' + 'Beta'.center(6) + ']')

Ejecuta el código anterior y verifica qué salidas produce.

[Beta]
[Beta]
[ Beta ]

La variante de dos parámetros de  center()  hace uso del caracter del segundo
argumento, en lugar de un espacio. Analiza el siguiente ejemplo:

print('[' + 'gamma'.center(20, '*') + ']')

Es por eso que la salida ahora se ve así:

[*******gamma********]

Realiza más experimentos.

El método endswith()
El método  endswith()  comprueba si la cadena dada termina con el argumento
especificado y devuelve  True (verdadero)  o  False (falso) , dependiendo del
resultado.

Nota: la subcadena debe adherirse al último carácter de la cadena; no se puede ubicar


en algún lugar cerca del final de la cadena.

Observa el ejemplo en el editor, analizalo y ejecútalo. Produce:

# Demostración del método endswith()

if "epsilon".endswith("on"):
print("si")

else:

print("no")

si

Ahora deberías poder predecir la salida del fragmento de código a continuación:

t = "zeta"
print(t.endswith("a"))
print(t.endswith("A"))
print(t.endswith("et"))
print(t.endswith("eta"))

Ejecuta el código para verificar tus predicciones.

El método find()
El método  find()  es similar al método  index() , el cual ya conoces - busca una
subcadena y devuelve el índice de la primera aparición de esta subcadena, pero:

 Es más seguro, no genera un error para un argumento que contiene una


subcadena inexistente (devuelve  -1  en dicho caso).
 Funciona solo con cadenas - no intentes aplicarlo a ninguna otra secuencia.

Analiza el código en el editor. Así es como puedes usarlo.

# Demostración del método find()

print("Eta".find("ta"))

print("Eta".find("mma"))

El ejemplo imprime:

-1

Nota: no se debe de emplear  find()  si deseas verificar si un solo carácter aparece
dentro de una cadena - el operador  in  será significativamente más rápido.

Aquí hay otro ejemplo:


t = 'teta'

print(t.find('eta'))

print(t.find('et'))

print(t.find('te'))

print(t.find('ha'))

¿Puedes predecir la salida? Ejecútalo y verifica tus predicciones.

1
1
0
-1

Si deseas realizar la búsqueda, no desde el principio de la cadena, sino desde


cualquier posición, puedes usar una variante de dos parámetros del
método  find() . Mira el ejemplo:

print('kappa'.find('a', 2))

El segundo argumento especifica el índice en el que se iniciará la búsqueda.

De las dos letras a, solo se encontrará la segunda. Ejecuta el código y verifica.

Se puede emplear el método  find()  para buscar todas las ocurrencias de la


subcadena, como aquí:

txt = """A variation of the ordinary lorem ipsum

text has been used in typesetting since the 1960s

or earlier, when it was popularized by advertisements

for Letraset transfer sheets. It was introduced to

the Information Age in the mid-1980s by the Aldus Corporation,

which employed it in graphics and word-processing templates

for its desktop publishing program PageMaker (from Wikipedia)"""

fnd = txt.find('the')

while fnd != -1:

print(fnd)
fnd = txt.find('the', fnd + 1)

El código imprime los índices de todas las ocurrencias del artículo the, y su salida se ve
así:

15

80

198

221

238

Existe también una mutación de tres parámetros del método  find()  - el tercer


argumento apunta al primer índice que no se tendrá en cuenta durante la
búsqueda (en realidad es el límite superior de la búsqueda).

Observa el ejemplo a continuación:

print('kappa'.find('a', 1, 4))

print('kappa'.find('a', 2, 4))

El segundo argumento especifica el índice en el que se iniciará la búsqueda (no tiene


que caber dentro de la cadena).

Por lo tanto, las salidas de ejemplo son:

-1

a no se puede encontrar dentro de los límites de búsqueda dados en el


segundo  print() .

El método isalnum()
El método sin parámetros llamado  isalnum()  comprueba si la cadena contiene
solo dígitos o caracteres alfabéticos (letras) y devuelve  True
(verdadero)  o  False (falso)  de acuerdo al resultado.

Observa el ejemplo en el editor y ejecútalo.

# Demostración del método the isalnum()


print('lambda30'.isalnum())

print('lambda'.isalnum())

print('30'.isalnum())

print('@'.isalnum())

print('lambda_30'.isalnum())

print(''.isalnum())

Nota: cualquier elemento de cadena que no sea un dígito o una letra hace que el
método regrese  False (falso) . Una cadena vacía también lo hace.

El resultado de ejemplo es:

True
True
True
False
False
False

Hay tres más ejemplos aquí:

t = 'Six lambdas'
print(t.isalnum())

False

t = 'ΑβΓδ'
print(t.isalnum())

True

t = '20E1'
print(t.isalnum())

True

Ejecútalos y verifica su salida.

Nota: la causa del primer resultado es un espacio, no es ni un dígito ni una letra.


El método isalpha()
El método  isalpha()  es más especializado, se interesa en letras solamente.

Observa el Ejemplo 1, su salida es:

# Ejemplo 1: Demostración del método isapha()

print("Moooo".isalpha())

print('Mu40'.isalpha())

True

False

El método isdigit()
Al contrario, el método  isdigit()  busca sólo dígitos - cualquier otra cosa
produce  False (falso)  como resultado.

Observa el Ejemplo 2, su salida es:

# Ejemplo 2: Demostración del método isdigit()

print('2018'.isdigit())

print("Año2019".isdigit())

True

False

El método islower()
El método  islower()  es una variante de  isalpha()  - solo acepta letras minúsculas.

Observa el Ejemplo 1 en el editor, genera:

# Ejemplo 1: Demostración del método islower()

print("Moooo".islower())

print('moooo'.islower())

False
True

El método isspace()
El método  isspace()  identifica espacios en blanco solamente - no tiene en cuenta
ningún otro caracter (el resultado es entonces  False ).

Observa el Ejemplo 2 en el editor, genera:

# Ejemplo 2: Demostración del método isspace()

print(' \n '.isspace())

print(" ".isspace())

print("mooo mooo mooo".isspace())

True

True

False

El método isupper()
El método  isupper()  es la versión en mayúscula de  islower()  - se concentra solo
en letras mayúsculas.

De nuevo, observa el Ejemplo 3 en el editor, genera:

# Ejemplo 3: Demostración del método isupper()

print("Moooo".isupper())

print('moooo'.isupper())

print('MOOOO'.isupper())

False

False

True
El método join()
El método  join()  es algo complicado, así que déjanos guiarte paso a paso:

 Como su nombre lo indica, el método realiza una unión y espera un


argumento del tipo lista; se debe asegurar que todos los elementos de la lista
sean cadenas: de lo contrario, el método generará una excepción TypeError.
 Todos los elementos de la lista serán unidos en una sola cadena pero...
 ...la cadena desde la que se ha invocado el método será utilizada como
separador, puesta entre las cadenas.
 La cadena recién creada se devuelve como resultado.

Echa un vistazo al ejemplo en el editor. Vamos a analizarlo:

# Demostración del método join()

print(",".join(["omicron", "pi", "rho"]))

 El método  join()  se invoca desde una cadena que contiene una coma (la
cadena puede ser larga o puede estar vacía).
 El argumento del  join  es una lista que contiene tres cadenas.
 El método devuelve una nueva cadena.

Aquí está:

omicron,pi,rh

El método lower()
El método  lower()  genera una copia de una cadena, reemplaza todas las letras
mayúsculas con sus equivalentes en minúsculas, y devuelve la cadena como
resultado. Nuevamente, la cadena original permanece intacta.

Si la cadena no contiene caracteres en mayúscula, el método devuelve la cadena


original.

Nota: El método  lower()  no toma ningún parámetro.

La salida del ejemplo del editor es:

# Demostración del método lower()

print("SiGmA=60".lower())

sigma=60

Como ya sabes, realiza tus propios experimentos.


El método lstrip()
El método sin parámetros  lstrip()  devuelve una cadena recién creada formada a
partir de la original eliminando todos los espacios en blanco iniciales.

Analiza el ejemplo en el editor.

# Demostración del método the lstrip()

print("[" + " tau ".lstrip() + "]")

Los corchetes no son parte del resultado, solo muestran los límites del resultado.

Las salida del ejemplo es:

[tau ]

El método con un parámetro  lstrip()  hace lo mismo que su versión sin


parámetros, pero elimina todos los caracteres incluidos en el argumento (una
cadena), no solo espacios en blanco:

print("www.cisco.com".lstrip("w."))

Aquí no se necesitan corchetes, ya que el resultado es el siguiente:

cisco.com

¿Puedes adivinar la salida del fragmento a continuación? Piensa cuidadosamente.


Ejecuta el código y verifica tus predicciones.

print("pythoninstitute.org".lstrip(".org"))

pythoninstitute.org

¿Sorprendido? Nuevamente, experimenta con tus propios ejemplos.

El método replace()
El método  replace()  con dos parámetros devuelve una copia de la cadena
original en la que todas las apariciones del primer argumento han sido
reemplazadas por el segundo argumento.

Analiza el código en el editor y ejecútalo.

# Demostración del método replace()

print("www.netacad.com".replace("netacad.com", "pythoninstitute.org"))
print("This is it!".replace("is", "are"))

print("Apple juice".replace("juice", ""))

El segundo argumento puede ser una cadena vacía (lo que hace es eliminar en lugar
de reemplazar), pero el primer argumento no puede estar vacío.

La salida del ejemplo es:

www.pyhoninstitute.org
Thare are it!
Apple

La variante del métdodo  replace()  con tres parámetros emplea un tercer


argumento (un número) para limitar el número de reemplazos.

Observa el código modificado a continuación:

print("This is it!".replace("is", "are", 1))


print("This is it!".replace("is", "are", 2))

¿Puedes adivinar su salida? Ejecuta el código y verifica tus conjeturas.

Thare is it!
Thare are it!

El método rfind()
Los métodos de uno, dos y tres parámetros denominados  rfind()  hacen casi lo
mismo que sus contrapartes (las que carecen del prefijo r), pero comienzan sus
búsquedas desde el final de la cadena, no el principio (de ahí el prefijo r, de reversa).

Echa un vistazo al código en el editor e intenta predecir su salida. Ejecuta el código


para verificar si tenías razón.

# Demostración del método rfind()

print("tau tau tau".rfind("ta"))

print("tau tau tau".rfind("ta", 9))

print("tau tau tau".rfind("ta", 3, 9))

8
-1
4
El método rstrip()
Dos variantes del método  rstrip()  hacen casi lo mismo que el método  lstrip ,
pero afecta el lado opuesto de la cadena.

Mira el ejemplo en el editor. ¿Puedes adivinar su salida? Ejecuta el código para verificar
tus conjeturas.

Como de costumbre, te recomendamos experimentar con tus propios ejemplos.

# Demostración del método rstrip()

print("[" + " upsilon ".rstrip() + "]")

print("cisco.com".rstrip(".com"))

[ upsilon]
cis

El método split()
El método  split()  divide la cadena y crea una lista de todas las subcadenas
detectadas.

El método asume que las subcadenas están delimitadas por espacios en blanco -


los espacios no participan en la operación y no se copian en la lista resultante.

Si la cadena está vacía, la lista resultante también está vacía.

Observa el código en el editor. El ejemplo produce el siguiente resultado:

# Demostración del método split()

print("phi chi\npsi".split())

['phi', 'chi', 'psi']

Nota: la operación inversa se puede realizar por el método  join() .

El método startswith()
El método  startswith()  es un espejo del método  endswith()  - comprueba si una
cadena dada comienza con la subcadena especificada.

Mira el ejemplo en el editor. Este es el resultado:

# Demostración del método startswith()


print("omega".startswith("meg"))

print("omega".startswith("om"))

print()

False

True

El método strip()
El método  strip()  combina los efectos causados por  rstrip()  y  lstrip()  - crea
una nueva cadena que carece de todos los espacios en blanco iniciales y finales.

Observa el segundo ejemplo en el editor. Este es el resultado que devuelve:

# Demostración del método strip()

print("[" + " aleph ".strip() + "]")

[aleph]

El método swapcase()
El método  swapcase()  crea una nueva cadena intercambiando todas las letras
por mayúsculas o minúsculas dentro de la cadena original: los caracteres en
mayúscula se convierten en minúsculas y viceversa.

Todos los demás caracteres permanecen intactos.

Observa el primer ejemplo en el editor. ¿Puedes adivinar la salida?

# Demostración del método swapcase()

print("Yo sé que no sé nada.".swapcase())

yO SÉ QUE NO SÉ NADA.

El método title()
El método  title()  realiza una función algo similar cambia la primera letra de cada
palabra a mayúsculas, convirtiendo todas las demás a minúsculas.

Mira el segundo ejemplo en el editor. ¿Puedes adivinar su salida? Este es el resultado:


# Demostración del método title()

print("Yo sé que no sé nada. Parte 1.".title())

Yo Sé Que No Sé Nada. Parte 1.

El método upper()
Por último, pero no menos importante, el método  upper()  hace una copia de la
cadena de origen, reemplaza todas las letras minúsculas con sus equivalentes en
mayúsculas, y devuelve la cadena como resultado.

Mira el tercer ejemplo en el editor. Produce:

# Demostración del método upper()

print("Yo sé que no sé nada. Parte 2.".upper())

YO SÉ QUE NO SÉ NADA. PARTE 2.

¡Hurra! Hemos llegado al final de esta sección. ¿Te sorprende alguno de los métodos
de cadena que hemos discutido hasta ahora? Toma un par de minutos para revisarlos
y pasemos a la siguiente parte del curso, donde te mostraremos qué cosas podemos
hacer con las cadenas.

 5.1.9.18 Tu propio split

LABORATORIO

Tiempo Estimado
20-25 minutos

Nivel de dificultad
Medio

Objetivos
 Mejorar las habilidades del alumno al trabajar con cadenas.
 Utilizar los métodos incorporados de Python para las cadenas.
Escenario
Ya sabes como funiona el método  split() . Ahora queremos que lo pruebes.

Tu tarea es escribir tu propia función, que se comporte casi como el método


original  split() , por ejemplo:

 Debe aceptar únicamente un argumento: una cadena.


 Debe devolver una lista de palabras creadas a partir de la cadena, dividida en
los lugares donde la cadena contiene espacios en blanco.
 Si la cadena está vacía, la función debería devolver una lista vacía.
 Su nombre debe ser  misplit() .

Usa la plantilla en el editor. Prueba tu código con cuidado.

def misplit(strng):

return strng.split()

print(misplit("Ser o no ser, esa es la pregunta"))

print(misplit("Ser o no ser,esa es la pregunta"))

print(misplit(" "))

print(misplit(" abc "))

print(misplit(""))

Salida esperada
['Ser', 'o', 'no', 'ser', 'esa', 'es,', 'la', 'pregunta']

['Ser', 'o', 'no', 'ser,esa', 'es', 'la', 'pregunta']

[]

['abc']

[]

Comparando cadenas
Las cadenas en Python pueden ser comparadas usando el mismo conjunto de
operadores que se emplean con los números.

Eche un vistazo a estos operadores: también pueden comparar cadenas:

 ==
 !=
 >
 >=
 <
 <=

Existe un "pero": los resultados de tales comparaciones a veces pueden ser un poco
sorprendentes. No olvides que Python no es consciente (no puede ser de ninguna
manera) de problemas lingüísticos sutiles, simplemente compara valores de puntos
de código, caracter por caracter.

Los resultados que obtienen de una operación de este tipo a veces son sorprendentes.
Comencemos con los casos más simples.

Dos cadenas son iguales cuando consisten en los mismos caracteres en el mismo
orden. Del mismo modo, dos cadenas no son iguales cuando no consisten en los
mismos caracteres en el mismo orden.

Ambas comparaciones dan  True  (verdadero) como resultado:

'alfa' == 'alfa'

'alfa' != 'Alfa'

La relación final entre cadenas está determinada por comparar el primer caracter


diferente en ambas cadenas (ten en cuenta los puntos de código ASCII / UNICODE en
todo momento).

Cuando se comparan dos cadenas de diferentes longitudes y la más corta es idéntica a


la más larga, la cadena más larga se considera mayor.

Justo como aquí:

'alfa' < 'alfabeto'

La relación es  True  (verdadera).

La comparación de cadenas siempre distingue entre mayúsculas y minúsculas (las


letras mayúsculas se consideran menores en comparación con las minúsculas).

La expresión es  True  (verdadera):

'beta' > 'Beta'


Comparando cadenas: continuación
Aún si una cadena contiene solo dígitos, todavía no es un número. Se interpreta
como lo que es, como cualquier otra cadena regular, y su aspecto numérico (potencial)
no se toma en cuenta, en ninguna manera.

Mira los ejemplos:

'10' == '010'
'10' > '010'
'10' > '8'
'20' < '8'
'20' < '80'

Producen los siguientes resultados:

False
True
False
True
True

Comparar cadenas contra números generalmente es una mala idea.

Las únicas comparaciones que puede realizar con impunidad son aquellas
simbolizadas por los operadores  ==  y  != . El primero siempre devuelve  False ,
mientras que el segundo siempre devuelve  True .

El uso de cualquiera de los operadores de comparación restantes generará una


excepción TypeError.

Vamos a verlo:

'10' == 10
'10' != 10
'10' == 1
'10' != 1
'10' > 10

Los resultados en este caso son:

False
True
False
True
TypeError exception
Ordenamiento
La comparación está estrechamente relacionada con el ordenamiento (o más bien, el
ordenamiento es, de hecho, un caso muy sofisticado de comparación).

Esta es una buena oportunidad para mostrar dos formas posibles de ordenar listas
que contienen cadenas. Dicha operación es muy común en el mundo real: cada vez
que ves una lista de nombres, productos, títulos o ciudades, esperas que este
ordenada.

Supongamos que deseas ordenar la siguiente lista:

greek = ['omega', 'alfa', 'pi', 'gama']

En general, Python ofrece dos formas diferentes de ordenar las listas.

El primero se implementa con una función llamada  sorted() .

La función toma un argumento (una lista) y devuelve una nueva lista, con los
elementos ordenados del argumento. (Nota: esta descripción está un poco
simplificada en comparación con la implementación real; lo discutiremos más
adelante).

La lista original permanece intacta.

Mira el código en el editor y ejecútalo. El fragmento produce el siguiente resultado:

# Demostración de la función sorted()

firstGreek = ['omega', 'alfa', 'pi', 'gama']

firstGreek2 = sorted(firstGreek)

print(firstGreek)

print(firstGreek2)

print()

['omega', 'alfa', 'pi', 'gama']


['alfa', 'gama', 'omega', 'pi']

El segundo método afecta a la lista misma - no se crea una nueva lista. El
ordenamiento se realiza por el método denominado  sort() .

# Demostración del método sort()


secondGreek = ['omega', 'alfa', 'pi', 'gama']

print(secondGreek)

secondGreek.sort()

print(secondGreek)

El resultado no ha cambiado:

['omega', 'alfa', 'pi', 'gama']


['alfa', 'gama', 'omega', 'pi']

Si necesitas un orden que no sea descendente, debes convencer a la función o método


de cambiar su comportamiento predeterminado. Lo discutiremos pronto.

Cadenas contra números


Hay dos cuestiones adicionales que deberían discutirse aquí: cómo convertir un
número (un entero o un flotante) en una cadena, y viceversa. Puede ser necesario
realizar tal transformación. Además, es una forma rutinaria de procesar datos de
entrada o salida.

La conversión de cadena a número es simple, ya que siempre es posible. Se realiza


mediante una función llamada  str() .

Justo como aquí:

itg = 13
flt = 1.3
si = str(itg)
sf = str(flt)

print(si + ' ' + sf)

La salida del código es:

13 1.3

La transformación inversa solo es posible cuando la cadena representa un número


válido. Si no se cumple la condición, espera una excepción ValueError.

Emplea la función  int()  si deseas obtener un entero, y  float()  si necesitas un valor
punto flotante.

Justo como aquí:


si = '13'
sf = '1.3'
itg = int(si)
flt = float(sf)

print(itg + flt)

Esto es lo que verás en la consola:

14.3

En la siguiente sección, te mostraremos algunos programas simples que procesan


cadenas.

 5.1.10.6 LABORATORIO: Un Display LED

LABORATORIO

Tiempo Estimado
30 minutos

Nivel de dificultad
Medio

Objetivos
 Mejorar las habilidades del alumno para trabajar con cadenas.
 Usar cadenas para representar datos que no son texto.

Escenario
Seguramente has visto un display de siete segmentos.

Es un dispositivo (a veces electrónico, a veces mecánico) diseñado para presentar un


dígito decimal utilizando un subconjunto de siete segmentos. Si aún no sabes lo qué
es, consulta la siguiente liga en Wikipedia artículo.

Tu tarea es escribir un programa que puede simular el funcionamiento de un


display de siete segmentos, aunque vas a usar LEDs individuales en lugar de
segmentos.

Cada dígito es construido con 13 LEDs (algunos iluminados, otros apagados, por
supuesto), así es como lo imaginamos:
# ### ### # # ### ### ### ### ### ###

# # # # # # # # # # # # # #

# ### ### ### ### ### # ### ### # #

# # # # # # # # # # # # #

# ### ### # ### ### # ### ### ###

Nota: el número 8 muestra todas las luces LED encendidas.

Tu código debe mostrar cualquier número entero no negativo ingresado por el


usuario.

Consejo: puede ser muy útil usar una lista que contenga patrones de los diez dígitos.

Datos de prueba
Entrada de muestra:

123

Salida de muestra:

# ### ###

# # #

# ### ###

# # #

# ### ###

Entrada de muestra:

9081726354

Salida de muestra:

### ### ### # ### ### ### ### ### # #

# # # # # # # # # # # # # #

### # # ### # # ### ### ### ### ###

# # # # # # # # # # # # #
### ### ### # # ### ### ### ### #

El Cifrado César: encriptando un mensaje


Te mostraremos cuatro programas simples para presentar algunos aspectos del
procesamiento de cadenas en Python. Son intencionalmente simples, pero los
problemas de laboratorio serán significativamente más complicados.

El primer problema que queremos mostrarte se llama Cifrado César - más detalles


aquí: https://en.wikipedia.org/wiki/Caesar_cipher.

Este cifrado fue (probablemente) inventado y utilizado por Cayo Julio César y sus
tropas durante las Guerras Galas. La idea es bastante simple: cada letra del mensaje
se reemplaza por su consecuente más cercano (A se convierte en B, B se convierte
en C, y así sucesivamente). La única excepción es Z, la cual se convierte en A.

El programa en el editor es una implementación muy simple (pero funcional) del


algoritmo.

# Cifrado César

text = input("Ingresa tu mensaje: ")

cifrado = ''

for char in text:

if not char.isalpha():

continue

char = char.upper()

code = ord(char) + 1

if code > ord('Z'):

code = ord('A')

cifrado += chr(code)

print(cifrado)

Se ha escrito utilizando los siguientes supuestos:

 Solo acepta letras latinas (nota: los romanos no usaban espacios en blanco ni
dígitos).
 Todas las letras del mensaje están en mayúsculas (nota: los romanos solo
conocían las mayúsculas).

Veamos el código:

 La línea 02: pide al usuario que ingrese un mensaje (sin cifrar) de una línea.
 La línea 03: prepara una cadena para el mensaje cifrado (esta vacía por ahora).
 La línea 04: inicia la iteración a través del mensaje.
 La línea 05: si el caracter actual no es alfabético...
 La línea 06: ...ignoralo.
 La línea 07: convierta la letra a mayúsculas (es preferible hacerlo a ciegas, en
lugar de verificar si es necesario o no).
 La línea 08: obtén el código de la letra e increméntalo en uno.
 La línea 09: si el código resultante ha "dejado" el alfabeto latino (si es mayor
que el código de la Z)...
 La línea 10: ... cámbialo al código de la A.
 La línea 11: agrega el carácter recibido al final del mensaje cifrado.
 La línea 13: imprime el cifrado.

El código, alimentado con este mensaje:

AVE CAESAR

Da como salida:

BWFDBFTBS

El cifrado César: descifrando un mensaje


La operación inversa ahora debería ser clara para ti: solo presentamos el código tal
como está, sin ninguna explicación.

Observa el código en el editor. Comprueba cuidadosamente si funciona. Usa el


criptograma del programa anterior.

# Cifrado César - descifrar un mensaje


cifrado = input('Ingresa tu criptograma: ')
text = ''
for char in cifrado:
if not char.isalpha():
continue
char = char.upper()
code = ord(char) - 1
if code < ord('A'):
code = ord('Z')
text += chr(code)

print(text)

El Procesador de Números
El tercer programa muestra un método simple que permite ingresar una línea llena de
números y sumarlos fácilmente. Nota: la función  input() , combinada junto con las
funciones  int()  o  float() , no es lo adecuado para este propósito.

El procesamiento será extremadamente fácil: queremos que se sumen los números.

Observa el código en el editor. Analicémoslo.

#Procesador de números

linea = input("Ingresa una línea de números, sepáralos con espacios: ")

strings = linea.split()

total = 0

try:

for substr in strings:

total += float(substr)

print("El total es:", total)

except:

print(substr, "no es un numero.")

Emplear listas puede hacer que el código sea más pequeño. Puedes hacerlo si quieres.

Presentemos nuestra versión:

 La línea 03: pide al usuario que ingrese una línea llena de cualquier cantidad de
números (los números pueden ser flotantes).
 La línea 04: divide la línea en una lista con subcadenas.
 La línea 05: se inicializa la suma total a cero.
 La línea 06: como la conversión de cadena a flotante puede generar una
excepción, es mejor continuar con la protección del bloque try-except.
 La línea 07: itera a través de la lista...
 La línea 08: ...e intenta convertir todos sus elementos en números flotantes; si
funciona, aumenta la suma.
 La línea 09: todo está bien hasta ahora, así que imprime la suma.
 La línea 10: el programa termina aquí en caso de error.
 La línea 11: imprime un mensaje de diagnóstico que muestra al usuario el
motivo del error.

El código tiene una debilidad importante: muestra un resultado falso cuando el


usuario ingresa una línea vacía. ¿Puedes arreglarlo?

El Validador IBAN
El cuarto programa implementa (en una forma ligeramente simplificada) un algoritmo
utilizado por los bancos europeos para especificar los números de cuenta. El estándar
llamado IBAN (Número de cuenta bancaria internacional) proporciona un método
simple y bastante confiable para validar los números de cuenta contra errores
tipográficos simples que pueden ocurrir durante la reescritura del número, por
ejemplo, de documentos en papel, como facturas o facturas en las computadoras.

Puedes encontrar más detalles


aquí: https://en.wikipedia.org/wiki/International_Bank_Account_Number.

Un número de cuenta compatible con IBAN consta de:

 Un código de país de dos letras tomado del estándar ISO 3166-1 (por
ejemplo, FR para Francia, GB para Gran Bretaña DE para Alemania, y así
sucesivamente).
 Dos dígitos de verificación utilizados para realizar las verificaciones de validez:
pruebas rápidas y simples, pero no totalmente confiables, que muestran si un
número es inválido (distorsionado por un error tipográfico) o valido.
 El número de cuenta real (hasta 30 caracteres alfanuméricos; la longitud de esa
parte depende del país).

El estándar dice que la validación requiere los siguientes pasos (según Wikipedia):

 (Paso 1) Verificar que la longitud total del IBAN sea correcta según el país (este
programa no lo hará, pero puedes modificar el código para cumplir con este
requisito si lo deseas; nota: pero debes enseñar al código a conocer todas las
longitudes utilizadas en Europa).
 (Paso 2) Mueve los cuatro caracteres iniciales al final de la cadena (es decir, el
código del país y los dígitos de verificación).
 (Paso 3) Reemplaza cada letra en la cadena con dos dígitos, expandiendo así la
cadena, donde A = 10, B = 11 ... Z = 35.
 (Paso 4) Interpreta la cadena como un entero decimal y calcula el residuo de
ese número dividiendolo entre 97. Si el residuo es 1, pasa la prueba de
verificación de dígitos y el IBAN puede ser válido.

Observa el código en el editor. Analicémoslo:


# Validador IBAN

iban = input("Ingresa IBAN, por favor: ")

iban = iban.replace(' ','')

if not iban.isalnum():

print("Has introducido caracteres no válidos.")

elif len(iban) < 15:

print("El IBAN ingresado es demasiado corto.")

elif len(iban) > 31:

print("El IBAN ingresado es demasiado largo.")

else:

iban = (iban[4:] + iban[0:4]).upper()

iban2 = ''

for ch in iban:

if ch.isdigit():

iban2 += ch

else:

iban2 += str(10 + ord(ch) - ord('A'))

ibann = int(iban2)

if ibann % 97 == 1:

print("El IBAN ingresado es válido.")

else:

print("El IBAN ingresado no es válido.")

 Línea 03: pide al usuario que ingrese el IBAN (el número puede contener
espacios, ya que mejoran significativamente la legibilidad del número...
 Línea 04: ...pero remueve los espacios de inmediato).
 Línea 05: el IBAN ingresado debe constar solo de dígitos y letras, de lo
contrario...
 Línea 06: ...muestra un mensaje.
 Línea 07: el IBAN no debe tener menos de 15 caracteres (esta es la variante
más corta, utilizada en Noruega).
 Línea 08: si es más corto, se informa al usuario.
 Línea 09: además, el IBAN no puede tener más de 31 caracteres (esta es la
variante más larga, utilizada en Malta).
 Línea 10: si es más largo, se le informa al usuario.
 Línea 11: se comienza con el procesamiento.
 Línea 12: se mueven los cuatro caracteres iniciales al final del número y se
convierten todas las letras a mayúsculas (paso 02 del algoritmo).
 Línea 13: esta es la variable utilizada para completar el número, creada al
reemplazar las letras con dígitos (de acuerdo con el paso 03 del algoritmo).
 Línea 14: iterar a través del IBAN.
 Línea 15: si el caracter es un digito...
 Línea 16: se copia.
 Línea 17: de lo contrario...
 Línea 18: ...conviértelo en dos dígitos (observa cómo se hace aquí).
 Línea 19: la forma convertida del IBAN está lista: ahora se convierte en un
número entero.
 Línea 20: ¿el residuo de la división de  iban2  entre  97  es igual a  1 ?
 Línea 21: si es así, entonces el número es correcto.
 Línea 22: de lo contrario...
 Línea 23: ...el número no es válido.

Agreguemos algunos datos de prueba (todos estos números son válidos; puedes
invalidarlos cambiando cualquier carácter).

 Inglés:  GB72 HBZU 7006 7212 1253 00


 Francés:  FR76 30003 03620 00020216907 50
 Alemán:  DE02100100100152517108

Si eres residente de la UE, puedes usar tu propio número de cuenta para hacer
pruebas.

 5.1.11.6 LABORATORIO: Mejorando el cifrado César

LABORATORIO
Tiempo Estimado
30-45 minutos

Nivel de Dificultad
Difícil

Prerrequisitos
Módulo 5.1.11.1, Módulo 5.1.11.2

Objetivos
 Mejorar las habilidades del alumno para manipular cadenas.
 Convertir caracteres en código ASCII y viceversa.

Escenario
Ya estás familiarizado con el cifrado César, y es por eso que queremos que mejores el
código que te mostramos recientemente.

El cifrado César original cambia cada caracter por otro a se convierte en b, z se
convierte en a, y así sucesivamente. Hagámoslo un poco más difícil y permitamos que
el valor desplazado provenga del rango 1 al 25.

Además, dejemos que el código conserve las mayúsculas y minúsculas (las minúsculas
permanecerán en minúsculas) y todos los caracteres no alfabéticos deben permanecer
intactos.

Tu tarea es escribir un programa el cual:

 Le pida al usuario una línea de texto para encriptar.


 Le pida al usuario un valor de cambio (un número entero del rango 1 al 25,
nota: debes obligar al usuario a ingresar un valor de cambio válido (¡no te
rindas y no dejes que los datos incorrectos te engañen!).
 Imprime el texto codificado.

Prueba tu código utilizando los datos que te proporcionamos.

Datos de Prueba
Entrada Muestra:

abcxyzABCxyz 123
2

Salida Muestra:

cdezabCDEzab 123

Entrada Muestra:

The die is cast

25

Salida Muestra:

Sgd chd hr bzrs

 5.1.11.7 LABORATORIO: Palíndromos

LABORATORIO

Tiempo Estimado
10-15 minutos

Nivel de Dificultad
Fácil

Objetivos
 Mejorar las habilidades del alumno para manipular cadenas.
 Alentar al alumno a buscar soluciones no obvias.

Escenario
¿Sabes qué es un palíndromo?

Es una palabra que se ve igual cuando se lee hacia adelante y hacia atrás. Por ejemplo,
"kayak" es un palíndromo, mientras que "leal" no lo es.

Tu tarea es escribir un programa que:

 Le pida al usuario algún texto.


 Compruebe si el texto introducido es un palíndromo e imprima el resultado.

Nota:

 Supón que una cadena vacía no es un palíndromo.


 Trata las letras mayúsculas y minúsculas como iguales.
 Los espacios no se toman en cuenta durante la verificación: trátalos como
inexistentes.
 Existe más de una solución correcta: intenta encontrar más de una.

Prueba tu código utilizando los datos que te proporcionamos.

Datos de Prueba
Entrada Muestra:

Ten animals I slam in a net

Salida Muestra:

Es un palíndromo

Entrada Muestra:

Eleven animals I slam in a net

Salida Muestra:

No es un palíndromo

 5.1.11.8 LABORATORIO: Anagramas

LABORATORIO

Tiempo Estimado
10-15 minutos
Nivel de Dificultad
Fácil

Objetivos
 Mejorar las habilidades del alumno para trabajar con cadenas.
 Convertir cadenas en listas, y viceversa.

Escenario
Un anagrama es una nueva palabra formada al reorganizar las letras de una palabra,
usando todas las letras originales exactamente una vez. Por ejemplo, las frases "rail
safety" y "fairy tales" son anagramas, mientras que "I am" y "You are" no lo son.

Tu tarea es escribir un programa que:

 Le pida al usuario dos textos por separado.


 Compruebe si los textos ingresados son anagramas e imprima el resultado.

Nota:

 Supongamos que dos cadenas vacías no son anagramas.


 Tratar las letras mayúsculas y minúsculas como iguales.
 Los espacios no se toman en cuenta durante la verificación: trátalos como
inexistentes.

Prueba tu código utilizando los datos que te proporcionamos.

Datos de Prueba
Entrada de Ejemplo:

Listen

Silent

Salida del Ejemplo:

Anagramas

Entrada de Ejemplo:

modern
norman

Salida del Ejemplo:

No son Anagramas

 5.1.11.9 LABORATORIO: El Dígito de la Vida

LABORATORIO

Tiempo Estimado
10-15 minutos

Nivel de Dificultad
Fácil

Objetivos
 Mejorar las habilidades del alumno para trabajar con cadenas.
 Convertir enteros en cadenas, y viceversa.

Escenario
Algunos dicen que el Dígito de la Vida es un dígito calculado usando el cumpleaños de
alguien. Es simple: solo necesitas sumar todos los dígitos de la fecha. Si el resultado
contiene más de un dígito, se debe repetir la suma hasta obtener exactamente un
dígito. Por ejemplo:

 1 Enero 2017 = 2017 01 01


 2 + 0 + 1 + 7 + 0 + 1 + 0 + 1 = 12
 1+2=3

3 es el dígito que buscamos y encontramos.

Tu tarea es escribir un programa que:

 Le pregunta al usuario su cumpleaños (en el formato AAAAMMDD o AAAADMM


o MMDDAAAA; en realidad, el orden de los dígitos no importa).
 Da como salida El Dígito de la Vida para la fecha ingresada.

Prueba tu código utilizando los datos que te proporcionamos.


Datos de Prueba
Entrada de Ejemplo:

19991229

Salida del Ejemplo:

Entrada de Ejemplo:

20000101

Salida del Ejemplo:

 5.1.11.10 LABORATIORIO: ¡Encuentra una palabra!

LABORATORIO

Tiempo Estimado
15-20 minutos

Nivel de Dificultad
Medio

Objetivos
 Mejorar las habilidades del alumno para trabajar con cadenas.
 Emplear el método  find()  para realizar busquedas dentro de las cadenas.

Escenario
Vamos a jugar un juego. Le daremos dos cadenas: una es una palabra (por ejemplo,
"dog") y la segunda es una combinación de un grupo de caracteres.
Tu tarea es escribir un programa que responda la siguiente pregunta: ¿Los caracteres
que comprenden la primera cadena están ocultos dentro de la segunda cadena?

Por ejemplo:

 Si la segunda cadena es "vcxzxduybfdsobywuefgas", la respuesta es  si .


 Si la segunda cadena es "vcxzxdcybfdstbywuefsas", la respuesta es  no  (ya
que no están las letras "d", "o", "g", ni en ese orden).

Consejos:

 Debes usar las variantes de dos argumentos de las funciones  pos()  dentro de
tu código.
 No te preocupes por mayúsculas y minúsculas.

Prueba tu código utilizando los datos que te proporcionamos.

Datos de Prueba
Entrada de Ejemplo:

donor

Nabucodonosor

Salida del Ejemplo:

Si

Entrada de Ejemplo:

donut

Nabucodonosor

Salida del Ejemplo:

No

 5.1.11.11 LABORATORIO: Sudoku

LABORATORIO
Tiempo Estimado
60 minutos

Nivel de Dificultad
Difícil

Objetivos
 Mejorar las habilidades del estudiante para trabajar con cadenas y listas.
 Convertir cadenas en listas.

Escenario
Como probablemente sabes, Sudoku es un rompecabezas de colocación de números
jugado en un tablero de 9x9. El jugador tiene que llenar el tablero de una manera muy
específica:

 Cada fila del tablero debe contener todos los dígitos del 0 al 9 (el orden no
importa).
 Cada columna del tablero debe contener todos los dígitos del 0 al 9
(nuevamente, el orden no importa).
 Cada subcuadro de 3x3 de la tabla debe contener todos los dígitos del 0 al 9.

Si necesitas más detalles, puedes encontrarlos aquí.

Tu tarea es escribir un programa que:

 Lea las 9 filas del Sudoku, cada una con 9 dígitos (verifica cuidadosamente si los
datos ingresados son válidos).
 Da como salida  Si  si el Sudoku es válido y  No  de lo contrario.

Prueba tu código utilizando los datos que te proporcionamos.

Datos de Prueba
Entrada de Muestra:

295743861

431865927

876192543

387459216

612387495
549216738

763524189

928671354

154938672

Salida de la Muestra:

Yes

Entrada de Muestra:

195743862

431865927

876192543

387459216

612387495

549216738

763524189

928671354

254938671

Salida de la Muestra

No

 6.1.1.2 Los fundamentos de la POO

Los conceptos básicos del enfoque orientado a


objetos
Demos un paso fuera de la programación y las computadoras, y analicemos temas de programación
orientada a objetos.

Casi todos los programas y técnicas que has utilizado hasta ahora pertenecen al estilo de
programación procedimental. Es cierto que has utilizado algunos objetos incorporados, pero cuando
nos referimos a ellos, se mencionan lo mínimo posible.

La programación procedimental fue el enfoque dominante para el desarrollo de software durante


décadas de TI, y todavía se usa en la actualidad. Además, no va a desaparecer en el futuro, ya que
funciona muy bien para proyectos específicos (en general, no muy complejos y no grandes, pero
existen muchas excepciones a esa regla).

El enfoque orientado a objetos es bastante joven (mucho más joven que el enfoque procedimental) y
es particularmente útil cuando se aplica a proyectos grandes y complejos llevados a cabo por grandes
equipos formados por muchos desarrolladores.

Este tipo de programación en un proyecto facilita muchas tareas importantes, por ejemplo, dividir el
proyecto en partes pequeñas e independientes y el desarrollo independiente de diferentes elementos
del proyecto.

Python es una herramienta universal para la programación procedimental y orientada a


objetos. Se puede utilizar con éxito en ambas.

Además, puedes crear muchas aplicaciones útiles, incluso si no se sabe nada sobre clases y objetos,
pero debes tener en cuenta que algunos de los problemas (por ejemplo, el manejo de la interfaz
gráfica de usuario) puede requerir un enfoque estricto de objetos.

Afortunadamente, la programación orientada a objetos es relativamente simple.


Enfoque procedimental versus el enfoque orientado
a objetos
En el enfoque procedimental, es posible distinguir dos mundos diferentes y completamente
separados: el mundo de los datos y el mundo del código. El mundo de los datos está poblado con
variables de diferentes tipos, mientras que el mundo del código está habitado por códigos agrupados
en módulos y funciones.

Las funciones pueden usar datos, pero no al revés. Además, las funciones pueden abusar de los
datos, es decir, usar el valor de manera no autorizada (por ejemplo, cuando la función seno recibe el
saldo de una cuenta bancaria como parámetro).

Los datos no pueden usar funciones. ¿Pero es esto completamente cierto? ¿Hay algunos tipos
especiales de datos que pueden usar funciones?

Sí, los hay, los llamados métodos. Estas son funciones que se invocan desde dentro de los datos, no
junto con ellos. Si puedes ver esta distinción, has dado el primer paso en la programación de objetos.
El enfoque orientado a objetos sugiere una forma de pensar completamente diferente. Los datos y
el código están encapsulados juntos en el mismo mundo, divididos en clases.

Cada clase es como una receta que se puede usar cuando quieres crear un objeto útil. Puedes
producir tantos objetos como necesites para resolver tu problema.

Cada objeto tiene un conjunto de rasgos (se denominan propiedades o atributos; usaremos ambas
palabras como sinónimos) y es capaz de realizar un conjunto de actividades (que se denominan
métodos).

Las recetas pueden modificarse si son inadecuadas para fines específicos y, en efecto, pueden crearse
nuevas clases. Estas nuevas clases heredan propiedades y métodos de los originales, y generalmente
agregan algunos nuevos, creando nuevas herramientas más específicas.

Los objetos son encarnaciones de las ideas expresadas en clases, como un pastel de queso en tu
plato, es una encarnación de la idea expresada en una receta impresa en un viejo libro de cocina.

Los objetos interactúan entre sí, intercambian datos o activan sus métodos. Una clase construida
adecuadamente (y, por lo tanto, sus objetos) puede proteger los datos sensibles y ocultarlos de
modificaciones no autorizadas.

No existe un límite claro entre los datos y el código: viven como uno solo dentro de los objetos.

Todos estos conceptos no son tan abstractos como pudieras pensar al principio. Por el contrario,
todos están tomados de experiencias de la vida real y, por lo tanto, son extremadamente útiles en la
programación de computadoras: no crean vida artificial reflejan hechos reales, relaciones y
circunstancias.
Jerarquías de clase
La palabra clases tiene muchos significados, pero no todos son compatibles con las ideas que
queremos discutir aquí. La clase que nos concierne es como una categoría, como resultado de
similitudes definidas con precisión.

Intentaremos señalar algunas clases que son buenos ejemplos de este concepto.

Veamos por un momento los vehículos. Todos los vehículos existentes (y los que aún no existen)
estan relacionados por una sola característica importante: la capacidad de moverse. Puedes
argumentar que un perro también se mueve; ¿Es un perro un vehículo? No lo es. Tenemos que
mejorar la definición, es decir, enriquecerla con otros criterios, distinguir los vehículos de otros seres
y crear una conexión más fuerte. Consideremos las siguientes circunstancias: los vehículos son
entidades creadas artificialmente que se utilizan para el transporte, movidos por fuerzas de la
naturaleza y dirigidos (conducidos) por humanos.

Según esta definición, un perro no es un vehículo.

La clase vehículos es muy amplia. Tenemos que definir clases especializadas. Las clases


especializadas son las subclases. La clase vehículos será una superclase para todas ellas.

Nota: la jerarquía crece de arriba hacia abajo, como raíces de árboles, no ramas. La clase más
general y más amplia siempre está en la parte superior (la superclase) mientras que sus descendientes
se encuentran abajo (las subclases).

A estas alturas, probablemente puedas señalar algunas subclases potenciales para la


superclase Vehículos. Hay muchas clasificaciones posibles. Elegimos subclases basadas en el medio
ambiente y decimos que hay (al menos) cuatro subclases:

 Vehículos Terrestres.
 Vehículos Acuáticos.
 Vehículos Aéreos.
 Vehículos Espaciales.

En este ejemplo, discutiremos solo la primera subclase: vehículos terrestres. Si lo deseas, puedes
continuar con las clases restantes.
Los vehículos terrestres pueden dividirse aún más, según el método con el que impactan el suelo.
Entonces, podemos enumerar:

 Vehículos de ruedas.
 Vehículos oruga.
 Aerodeslizadores.

La figura ilustra la jerarquía que hemos creado.

Ten en cuenta la dirección de las flechas: siempre apuntan a la superclase. La clase de nivel superior
es una excepción: no tiene su propia superclase.

Jerarquías de clase: continuación


Otro ejemplo es la jerarquía del reino taxonómico de los animales.

Podemos decir que todos los animales (nuestra clase de nivel superior) se puede dividir en cinco
subclases:

 Mamíferos.
 Reptiles.
 Pájaros.
 Peces.
 Anfibios.

Tomaremos el primero para un análisis más detallado.

Hemos identificado las siguientes subclases:

 Mamíferos salvajes.
 Mamíferos domesticados.

Intenta extender la jerarquía de la forma que quieras y encuentra el lugar adecuado para los
humanos.
¿Qué es un objeto?
Una clase (entre otras definiciones) es un conjunto de objetos. Un objeto es un ser perteneciente a
una clase.

Un objeto es una encarnación de los requisitos, rasgos y cualidades asignados a una clase
específica. Esto puede sonar simple, pero ten en cuenta las siguientes circunstancias importantes.
Las clases forman una jerarquía. Esto puede significar que un objeto que pertenece a una clase
específica pertenece a todas las superclases al mismo tiempo. También puede significar que
cualquier objeto perteneciente a una superclase puede no pertenecer a ninguna de sus subclases.

Por ejemplo: cualquier automóvil personal es un objeto que pertenece a la clase vehículos terrestres.
También significa que el mismo automóvil pertenece a todas las superclases de su clase local; por lo
tanto, también es miembro de la clase vehículos. Tu perro (o tu gato) es un objeto incluido en la
clase Mamíferos domesticados, lo que significa explícitamente que también está incluido en la
clase animales.

Cada subclase es más especializada (o más específica) que su superclase. Por el contrario,


cada superclase es más general (más abstracta) que cualquiera de sus subclases. Ten en cuenta que
hemos supuesto que una clase solo puede tener una superclase; esto no siempre es cierto, pero
discutiremos este tema más adelante.

Herencia
Definamos uno de los conceptos fundamentales de la programación de objetos, llamado herencia.
Cualquier objeto vinculado a un nivel específico de una jerarquía de clases hereda todos los rasgos
(así como los requisitos y cualidades) definidos dentro de cualquiera de las superclases.

La clase de inicio del objeto puede definir nuevos rasgos (así como requisitos y cualidades) que
serán heredados por cualquiera de sus superclases.

No deberías tener ningún problema para hacer coincidir esta regla con ejemplos específicos, ya sea
que se aplique a animales o vehículos.
¿Qué contiene un objeto?
La programación orientada a objetos supone que cada objeto existente puede estar equipado con
tres grupos de atributos:

 Un objeto tiene un nombre que lo identifica de forma exclusiva dentro de su namespace


(aunque también puede haber algunos objetos anónimos).
 Un objeto tiene un conjunto de propiedades individuales que lo hacen original, único o
sobresaliente (aunque es posible que algunos objetos no tengan propiedades).
 Un objeto tiene un conjunto de habilidades para realizar actividades específicas, capaz
de cambiar el objeto en sí, o algunos de los otros objetos.

Hay una pista (aunque esto no siempre funciona) que te puede ayudar a identificar cualquiera de las
tres esferas anteriores. Cada vez que se describe un objeto y se usa:

 Un sustantivo: probablemente se este definiendo el nombre del objeto.


 Un adjetivo: probablemente se este definiendo una propiedad del objeto.
 Un verbo: probablemente se este definiendo una actividad del objeto.

Dos ejemplos deberían servir como un buen ejemplo:

 Max es un gato grande que duerme todo el día.


Nombre del objeto = Max
Clase de inicio = Gato
Propiedad = Tamaño (grande)
Actividad = Dormir (todo el día)

 Un Cadillac rosa pasó rápidamente.


Nombre del objeto = Cadillac
Clase de inicio = Vehículo terrestre
Propiedad = Color (rosa)
Actividad = Pasar (rápidamente)
Tu primera clase
La programación orientada a objetos es el arte de definir y expandir clases. Una clase es un
modelo de una parte muy específica de la realidad, que refleja las propiedades y actividades que se
encuentran en el mundo real.

Las clases definidas al principio son demasiado generales e imprecisas para cubrir el mayor número
posible de casos reales.

No hay obstáculo para definir nuevas subclases más precisas. Heredarán todo de su superclase, por
lo que el trabajo que se utilizó para su creación no se desperdicia.

La nueva clase puede agregar nuevas propiedades y nuevas actividades y, por lo tanto, puede ser más
útil en aplicaciones específicas. Obviamente, se puede usar como una superclase para cualquier
número de subclases recién creadas.

El proceso no necesita tener un final. Puedes crear tantas clases como necesites.

La clase que se define no tiene nada que ver con el objeto: la existencia de una clase no significa
que ninguno de los objetos compatibles se creará automáticamente. La clase en sí misma no
puede crear un objeto: debes crearlo tu mismo y Python te permite hacerlo.

Es hora de definir la clase más simple y crear un objeto. Echa un vistazo al siguiente ejemplo:

class ClaseSimple:

pass

Hemos definido una clase. La clase es bastante pobre: no contiene propiedades ni actividades.
Esta vacía, pero eso no importa por ahora. Cuanto más simple sea la clase, mejor para nuestros
propósitos.
La definición comienza con la palabra clave reservada  class . La palabra clave reservada es
seguida por un identificador que nombrará la clase (nota: no lo confundas con el nombre del
objeto: estas son dos cosas diferentes).

A continuación, se agregan dos puntos:), como clases, como funciones, forman su propio bloque
anidado. El contenido dentro del bloque define todas las propiedades y actividades de la clase.

La palabra clave reservada  pass  llena la clase con nada. No contiene ningún método ni
propiedades.

Tu primer objeto
La clase recién definida se convierte en una herramienta que puede crear nuevos objetos. La
herramienta debe usarse explícitamente, bajo demanda.

Imagina que deseas crear un objeto (exactamente uno) de la clase  ClaseSimple .

Para hacer esto, debes asignar una variable para almacenar el objeto recién creado de esa clase y
crear un objeto al mismo tiempo.

Se hace de la siguiente manera:

miPrimerObjeto = ClaseSimple()

Nota:

 El nombre de la clase intenta fingir que es una función, ¿puedes ver esto? Lo discutiremos
pronto.
 El objeto recién creado está equipado con todo lo que trae la clase; Como esta clase está
completamente vacía, el objeto también está vacío.

El acto de crear un objeto de la clase seleccionada también se llama instanciación (ya que


el objeto se convierte en una instancia de la clase).

Dejemos las clases en paz por un breve momento, ya que ahora diremos algunas palabras
sobre pilas. Sabemos que el concepto de clases y objetos puede no estar completamente
claro todavía. No te preocupes, te explicaremos todo muy pronto.

¿Qué es una pila?


Una pila es una estructura desarrollada para almacenar datos de una manera muy específica..
Imagina una pila de monedas. No puedes poner una moneda en ningún otro lugar sino en la parte
superior de la pila. Del mismo modo, no puedes sacar una moneda de la pila desde ningún lugar que
no sea la parte superior de la pila. Si deseas obtener la moneda que se encuentra en la parte inferior,
debes eliminar todas las monedas de los niveles superiores.

El nombre alternativo para una pila (pero solo en la terminología de TI) es UEPS (LIFO son sus
siglas en íngles). Es una abreviatura para una descripción muy clara del comportamiento de la
pila: Último en Entrar - Primero en Salir (Last In - First Out). La moneda que quedó en último
lugar en la pila saldrá primero.

Una pila es un objeto con dos operaciones elementales, denominadas


convencionalmente push (cuando un nuevo elemento se coloca en la parte superior) y pop (cuando
un elemento existente se retira de la parte superior).

Las pilas se usan muy a menudo en muchos algoritmos clásicos, y es difícil imaginar la
implementación de muchas herramientas ampliamente utilizadas sin el uso de pilas.

Implementemos una pila en Python. Esta será una pila muy simple, y te mostraremos cómo hacerlo
en dos enfoques independientes: de manera procedimental y orientado a objetos.

Comencemos con el primero.

La pila: el enfoque procedimental


Primero, debes decidir cómo almacenar los valores que llegarán a la pila. Sugerimos
utilizar el método más simple, y emplear una lista para esta tarea. Supongamos que
el tamaño de la pila no está limitado de ninguna manera. Supongamos también que el
último elemento de la lista almacena el elemento superior.

La pila en sí ya está creada:

pila = []

Estamos listos para definir una función que pone un valor en la pila. Aquí están las
presuposiciones para ello:
 El nombre para la función es  push .
 La función obtiene un parámetro (este es el valor que se debe colocar en la
pila).
 La función no devuelve nada.
 La función agrega el valor del parámetro al final de la pila.

Así es como lo hemos hecho, echa un vistazo:

def push(val):
pila.append(val)

Ahora es tiempo de que una función quite un valor de la pila. Así es como puedes
hacerlo:

 El nombre de la función es pop.


 La función no obtiene ningún parámetro.
 La función devuelve el valor tomado de la pila.
 La función lee el valor de la parte superior de la pila y lo elimina.

La función esta aqui:

def pop():
val = pila[-1]
del pila[-1]
return val

Nota: la función no verifica si hay algún elemento en la pila.

Armemos todas las piezas juntas para poner la pila en movimiento. El programa
completo empuja (push) tres números a la pila, los saca e imprime sus valores en
pantalla. Puedes verlo en la ventana del editor.

El programa muestra el siguiente texto en pantalla:

1
2
3

Pruébalo.

pila = []

def push(val):
pila.append(val)
def pop():
val = pila[-1]
del pila[-1]
return val

push(3)
push(2)
push(1)

print(pop())
print(pop())
print(pop())

La pila: el enfoque procedimental frente al enfoque


orientado a objetos
La pila procedimental está lista. Por supuesto, hay algunas debilidades, y la implementación podría
mejorarse de muchas maneras (aprovechar las excepciones es una buena idea), pero en general la
pila está completamente implementada, y puedes usarla si lo necesitas.

Pero cuanto más la uses, más desventajas encontrarás. Éstas son algunas de ellas:

 La variable esencial (la lista de la pila) es altamente vulnerable; cualquiera puede


modificarla de forma incontrolable, destruyendo la pila; esto no significa que se haya hecho
de manera maliciosa; por el contrario, puede ocurrir como resultado de un descuido, por
ejemplo, cuando alguien confunde nombres de variables; imagina que accidentalmente has
escrito algo como esto:

pila[0] = 0
El funcionamiento de la pila estará completamente desorganizado.

 También puede suceder que un día necesites más de una pila; tendrás que crear otra lista
para el almacenamiento de la pila, y probablemente otras funciones  push  y  pop .

 También puede suceder que no solo necesites funciones  push  y  pop , pero también algunas
otras funciones; ciertamente podrías implementarlas, pero intenta imaginar qué sucedería si
tuvieras docenas de pilas implementadas por separado.

El enfoque orientado a objetos ofrece soluciones para cada uno de los problemas anteriores. Vamos a
nombrarlos primero:
 La capacidad de ocultar (proteger) los valores seleccionados contra el acceso no autorizado
se llama encapsulamiento; no se puede acceder a los valores encapsulados ni
modificarlos si deseas utilizarlos exclusivamente.

 Cuando tienes una clase que implementa todos los comportamientos de pila necesarios,
puedes producir tantas pilas como desees; no necesitas copiar ni replicar ninguna parte del
código.

 La capacidad de enriquecer la pila con nuevas funciones proviene de la herencia; puedes


crear una nueva clase (una subclase) que herede todos los rasgos existentes de la superclase
y agregue algunos nuevos.

Ahora escribamos una nueva implementación de pila desde cero. Esta vez, utilizaremos el enfoque
orientado a objetos, que te guiará paso a paso en el mundo de la programación de objetos.

La pila - el enfoque orientado a objetos


Por supuesto, la idea principal sigue siendo la misma. Usaremos una lista como
almacenamiento de la pila. Solo tenemos que saber cómo poner la lista en la clase.

Comencemos desde el principio: así es como comienza la pila de orientada a objetos:

class Pila:

Ahora, esperamos dos cosas de la clase:

 Queremos que la clase tenga una propiedad como el almacenamiento de la


pila - tenemos que "instalar" una lista dentro de cada objeto de la
clase (nota: cada objeto debe tener su propia lista; la lista no debe compartirse
entre diferentes pilas).
 Despues, queremos que la lista esté oculta de la vista de los usuarios de la
clase.

¿Cómo se hace esto?

A diferencia de otros lenguajes de programación, Python no tiene medios para


permitirte declarar una propiedad como esa.

En su lugar, debes agregar una instrucción específica. Las propiedades deben


agregarse a la clase manualmente.

¿Cómo garantizar que dicha actividad tiene lugar cada vez que se crea una nueva pila?

Hay una manera simple de hacerlo - tienes que equipar a la clase con una función
específica:

 Tiene que ser nombrada de forma estricta.


 Se invoca implícitamente cuando se crea el nuevo objeto.

Tal función es llamada el constructor, ya que su propósito general es construir un


nuevo objeto. El constructor debe saber todo acerca de la estructura del objeto y
debe realizar todas las inicializaciones necesarias.

Agreguemos un constructor muy simple a la nueva clase. Echa un vistazo al código:

class Pila:
def __init__(self):
print("¡Hola!")

objetoPila = Pila()

Expliquemos más a detalle:

 El nombre del constructor es siempre  __init__ .


 Tiene que tener al menos un parámetro (discutiremos esto más tarde); el
parámetro se usa para representar el objeto recién creado: puedes usar el
parámetro para manipular el objeto y enriquecerlo con las propiedades
necesarias; harás uso de esto pronto.
 Nota: el parámetro obligatorio generalmente se denomina  self  - es solo una
sugerencía, pero deberías seguirla - simplifica el proceso de lectura y
comprensión de tu código.

El código está en el editor. Ejecútalo ahora.

class Pila: # define la clase Pila

def __init__(self): # define la función del constructor


print("¡Hola!")

objetoPila = Pila() # instanciando el objeto

Aquí está su salida:

¡Hola!

Nota: no hay rastro de la invocación del constructor dentro del código. Ha sido
invocado implícita y automáticamente. Hagamos uso de eso ahora.

La pila - el enfoque orientado a objetos: continuación


Cualquier cambio que realices dentro del constructor que modifique el estado del
parámetro  self  se verá reflejado en el objeto recien creado.

Esto significa que puedes agregar cualquier propiedad al objeto y la propiedad


permanecerá allí hasta que el objeto termine su vida o la propiedad se elimine
explícitamente.

Ahora agreguemos solo una propiedad al nuevo objeto - una lista para la pila. La
nombraremos  listaPila .

Justo como aqui:

class Pila:
def __init__(self):
self.listaPila = []

objetoPila = Pila()
print(len(objetoPila.listaPila))

Nota:

 Hemos usado la notación punteada, al igual que cuando se invocan métodos.


Esta es la manera general para acceder a las propiedades de un objeto: debes
nombrar el objeto, poner un punto ( . ) después de el, y especificar el nombre
de la propiedad deseada, ¡no uses paréntesis! No deseas invocar un método,
deseas acceder a una propiedad.
 Si estableces el valor de una propiedad por primera vez (como en el
constructor), lo estás creando; a partir de ese momento, el objeto tiene la
propiedad y está listo para usar su valor.
 Hemos hecho algo más en el código: hemos intentado acceder a la
propiedad  listaPila  desde fuera de la clase inmediatamente después de que
se haya creado el objeto; queremos verificar la longitud actual de la pila, ¿lo
hemos logrado?

Sí, por supuesto: el código produce el siguiente resultado:

class Pila:

def __init__(self):

self.listaPila = []

objetoPila = Pila()

print(len(objetoPila.listaPila))

Esto no es lo que queremos de la pila. Nosotros queremos


que  listaPila  este escondida del mundo exterior. ¿Es eso posible?

Sí, y es simple, pero no muy intuitivo.

La pila - el enfoque orientado a objetos: continuación


Echa un vistazo: hemos agregado dos guiones bajos antes del nombre  listaPila  -
nada mas:

class Pila:

def __init__(self):

self.__listaPila = []

objetoPila = Pila()

print(len(objetoPila.__listaPila))

El cambio invalida el programa..

¿Por qué?

Cuando cualquier componente de la clase tiene un nombre que comienza con dos
guiones bajos ( __ ), se vuelve privado - esto significa que solo se puede acceder
desde la clase.
No puedes verlo desde el mundo exterior. Así es como Python implementa el concepto
de encapsulación.

Ejecuta el programa para probar nuestras suposiciones: una


excepción AttributeError debe ser lanzada.

class Pila:
def __init__(self):
self.listaPila = []

objetoPila = Pila()
print(len(objetoPila.__listaPila))

El enfoque orientado a objetos: una pila desde cero


Ahora es el momento de que las dos funciones (métodos) implementen las
operaciones push y pop. Python supone que una función de este tipo debería
estar inmersa dentro del cuerpo de la clase - como el constructor.

Queremos invocar estas funciones para  agregar (push)  y  quitar (pop)  valores de
la pila. Esto significa que ambos deben ser accesibles para el usuario de la clase (en
contraste con la lista previamente construida, que está oculta para los usuarios de la
clase ordinaria).

Tal componente es llamado publico, por ello no puede comenzar su nombre con


dos (o más) guiones bajos. Hay un requisito más - el nombre no debe tener más de
un guión bajo.

Las funciones en sí son simples. Echa un vistazo:

class Pila:
def __init__(self):
self.__listaPila = []

def push(self, val):


self.__listaPila.append(val)

def pop(self):
val = self.__listaPila[-1]
del self.__listaPila[-1]
return val

objetoPila = Pila()

objetoPila.push(3)
objetoPila.push(2)
objetoPila.push(1)
print(objetoPila.pop())
print(objetoPila.pop())
print(objetoPila.pop())

Sin embargo, hay algo realmente extraño en el código. Las funciones parecen
familiares, pero tienen más parámetros que sus contrapartes procedimentales.

Aquí, ambas funciones tienen un parámetro llamado  self  en la primera posición de la
lista de parámetros.

¿Es necesario? Si, lo es.

Todos los métodos deben tener este parámetro. Desempeña el mismo papel que el
primer parámetro constructor.

Permite que el método acceda a entidades (propiedades y actividades / métodos)


del objeto. No puedes omitirlo. Cada vez que Python invoca un método, envía
implícitamente el objeto actual como el primer argumento.

Esto significa que el método está obligado a tener al menos un parámetro, que
Python mismo utiliza - no tienes ninguna influencia sobre el.

Si tu método no necesita ningún parámetro, este debe especificarse de todos modos.


Si está diseñado para procesar solo un parámetro, debes especificar dos, ya que la
función del primero sigue siendo la misma.

Hay una cosa más que requiere explicación: la forma en que se invocan los métodos
desde la variable  __listaPila .

Afortunadamente, es mucho más simple de lo que parece:

 La primera etapa entrega el objeto como un todo →  self .


 A continuación, debes llegar a la lista  __listaPila  →  self.__listaPila .
 Con  __listaPila  lista para ser usada, puedes realizar el tercer y último paso
→  self.__listaPila.append(val) .

La declaración de la clase está completa y se han enumerado todos sus componentes.


La clase está lista para usarse.

El enfoque orientado a objetos: una pila desde cero


Tener tal clase abre nuevas posibilidades. Por ejemplo, ahora puedes hacer que más
de una pila se comporte de la misma manera. Cada pila tendrá su propia copia de
datos privados, pero utilizará el mismo conjunto de métodos.

Esto es exactamente lo que queremos para este ejemplo.


Analiza el código:

class Pila:

def __init__(self):

self.__listaPila = []

def push(self, val):

self.__listaPila.append(val)

def pop(self):

val = self.__listaPila[-1]

del self.__listaPila[-1]

return val

objetoPila1 = Pila()

objetoPila2 = Pila()

objetoPila1.push(3)

objetoPila2.push(objetoPila1.pop())

print(objetoPila2.pop())

Existen dos pilas creadas a partir de la misma clase base.


Trabajan independientemente. Puedes crear más si quieres.

Ejecuta el código en el editor y ve qué sucede. Realiza tus propios experimentos.

Resultado: 3

El enfoque orientado a objetos: una pila desde cero


(continuación)
Analiza el fragmento a continuación: hemos creado tres objetos de la clase  Pila .
Después, hemos hecho malabarismos. Intenta predecir el valor que se muestra en la
pantalla.

class Pila:

def __init__(self):

self.__listaPila = []

def push(self, val):

self.__listaPila.append(val)

def pop(self):

val = self.__listaPila[-1]

del self.__listaPila[-1]

return val

pequeñaPila = Pila()

otraPila = Pila()

graciosaPila = Pila()

pequeñaPila.push(1)

otraPila.push(pequeñaPila.pop() + 1)

graciosaPila.push(otraPila.pop() - 2)

print(graciosaPila.pop())

Entonces, ¿cuál es el resultado? Ejecuta el programa y comprueba si tenías razón.

Resultado: 0

El enfoque orientado a objetos: una pila desde cero


(continuación)
Ahora vamos un poco más lejos. Vamos a agregar una nueva clase para manejar
pilas.

La nueva clase debería poder evaluar la suma de todos los elementos almacenados


actualmente en la pila.

No queremos modificar la pila previamente definida. Ya es lo suficientemente buena


en sus aplicaciones, y no queremos que cambie de ninguna manera. Queremos una
nueva pila con nuevas capacidades. En otras palabras, queremos construir una
subclase de la ya existente clase  Pila .

El primer paso es fácil: solo define una nueva subclase que apunte a la clase que se
usará como superclase.

Así es como se ve:

class SumarPila(Pila):
pass

La clase aún no define ningún componente nuevo, pero eso no significa que esté
vacía. Obtiene (hereda) todos los componentes definidos por su superclase - el
nombre de la superclase se escribe después de los dos puntos, después del nombre
de la nueva clase.

Esto es lo que queremos de la nueva pila:

 Queremos que el método  push  no solo inserte el valor en la pila, sino que
también sume el valor a la variable  sum .
 Queremos que la función  pop  no solo extraiga el valor de la pila, sino que
también reste el valor de la variable  sum .

En primer lugar, agreguemos una nueva variable a la clase. Sera una variable privada,
al igual que la lista de pila. No queremos que nadie manipule el valor de la
variable  sum .

Como ya sabes, el constructor agrega una nueva propiedad a la clase. Ya sabes cómo
hacerlo, pero hay algo realmente intrigante dentro del constructor. Echa un vistazo:

class SumarPila(Pila):
def __init__(self):
Pila.__init__(self)
self.__sum = 0

La segunda línea del cuerpo del constructor crea una propiedad llamada  __sum  -
almacenará el total de todos los valores de la pila.
Pero la línea anterior se ve diferente. ¿Qué hace? ¿Es realmente necesaria? Sí lo es.

Al contrario de muchos otros lenguajes, Python te obliga a invocar explícitamente el


constructor de una superclase. Omitir este punto tendrá efectos nocivos: el objeto
se verá privado de la lista  __listaPila . Tal pila no funcionará correctamente.

Esta es la única vez que puedes invocar a cualquiera de los constructores disponibles
explícitamente; se puede hacer dentro del constructor de la superclase.

Ten en cuenta la sintaxis:

 Se especifica el nombre de la superclase (esta es la clase cuyo constructor se


desea ejecutar).
 Se pone un punto ( . ) después del nombre.
 Se especifica el nombre del constructor.
 Se debe señalar al objeto (la instancia de la clase) que debe ser inicializado por
el constructor; es por eso que se debe especificar el argumento y utilizar la
variable  self  aquí; recuerda: invocar cualquier método (incluidos los
constructores) desde fuera de la clase nunca requiere colocar el
argumento  self  en la lista de argumentos - invocar un método desde
dentro de la clase exige el uso explícito del argumento  self , y tiene que ser el
primero en la lista.

Nota: generalmente es una práctica recomendada invocar al constructor de la


superclase antes de cualquier otra inicialización que desees realizar dentro de la
subclase. Esta es la regla que hemos seguido en el código.

class Pila:
def __init__(self):
self.__listaPila = []

def push(self, val):


self.__listaPila.append(val)

def pop(self):
val = self.__listaPila[-1]
del self.__listaPila[-1]
return val

class SumarPila(Pila):
def __init__(self):
Pila.__init__(self)
self.__sum = 0
El enfoque orientado a objetos: una pila desde cero
(continuación)
En segundo lugar, agreguemos dos métodos. Pero, ¿realmente estamos agregándolos?
Ya tenemos estos métodos en la superclase. ¿Podemos hacer algo así?

Si podemos. Significa que vamos a cambiar la funcionalidad de los métodos, no sus


nombres. Podemos decir con mayor precisión que la interfaz (la forma en que se
manejan los objetos) de la clase permanece igual al cambiar la implementación al
mismo tiempo.

Comencemos con la implementación de la función  push . Esto es lo que esperamos de


la función:

 Agregar el valor a la variable  __sum .


 Agregar el valor a la pila.

Nota: la segunda actividad ya se implementó dentro de la superclase, por lo que


podemos usarla. Además, tenemos que usarla, ya que no hay otra forma de acceder a
la variable  __listaPila .

Así es como se mira el método  push  dentro de la subclase:

def push(self, val):


self.__sum += val
Pila.push(self, val)

Toma en cuenta la forma en que hemos invocado la implementación anterior del


método  push  (el disponible en la superclase):

 Tenemos que especificar el nombre de la superclase; esto es necesario para


indicar claramente la clase que contiene el método, para evitar confundirlo con
cualquier otra función del mismo nombre.
 Tenemos que especificar el objeto de destino y pasarlo como primer
argumento (no se agrega implícitamente a la invocación en este contexto).

Se dice que el método  push  ha sido anulado - el mismo nombre que en la superclase
ahora representa una funcionalidad diferente.

El enfoque orientado a objetos: una pila desde cero


(continuación)
Esta es la nueva función  pop :

def pop(self):
val = Pila.pop(self)

self.__sum -= val

return val

Hasta ahora, hemos definido la variable  __sum , pero no hemos proporcionado un


método para obtener su valor. Parece estar escondido. ¿Cómo podemos mostrarlo y
que al mismo tiempo se proteja de modificaciones?

Tenemos que definir un nuevo método. Lo nombraremos  getSuma . Su única tarea


será devolver el valor de  __sum .

Aquí está:

def getSuma(self):

return self.__sum

Entonces, veamos el programa en el editor. El código completo de la clase está ahí.


Podemos ahora verificar su funcionamiento, y lo hacemos con la ayuda de unas pocas
líneas de código adicionales.

Como puedes ver, agregamos cinco valores subsiguientes en la pila, imprimimos su


suma y los sacamos todos de la pila.

Bien, esta ha sido una breve introducción a la programación de orientada a objetos de


Python. Pronto te contaremos todo con más detalle.

class Pila:
def __init__(self):
self.__listaPila = []

def push(self, val):


self.__listaPila.append(val)

def pop(self):
val = self.__listaPila[-1]
del self.__listaPila[-1]
return val

class SumarPila(Pila):
def __init__(self):
Pila.__init__(self)
self.__sum = 0
def getSuma(self):
return self.__sum

def push(self, val):


self.__sum += val
Pila.push(self, val)

def pop(self):
val = Pila.pop(self)
self.__sum -= val
return val

objetoPila = SumarPila()

for i in range(5):
objetoPila.push(i)
print(objetoPila.getSuma())

for i in range(5):
print(objetoPila.pop())

10
4
3
2
1
0

 6.1.3.1 Propiedades de la POO

Variables de instancia
En general, una clase puede equiparse con dos tipos diferentes de datos para formar las propiedades
de una clase. Ya viste uno de ellos cuando estábamos estudiando pilas.

Este tipo de propiedad existe solo cuando se crea explícitamente y se agrega a un objeto. Como ya
sabes, esto se puede hacer durante la inicialización del objeto, realizada por el constructor.

Además, se puede hacer en cualquier momento de la vida del objeto. Es importante mencionar
también que cualquier propiedad existente se puede eliminar en cualquier momento.

Tal enfoque tiene algunas consecuencias importantes:


 Diferentes objetos de la misma clase pueden poseer diferentes conjuntos de propiedades.
 Debe haber una manera de verificar con seguridad si un objeto específico posee la
propiedad que deseas utilizar (a menos que quieras provocar una excepción, siempre vale
la pena considerarlo).
 Cada objeto lleva su propio conjunto de propiedades - no interfieren entre sí de ninguna
manera.

Tales variables (propiedades) se llaman variables de instancia.

La palabra instancia sugiere que están estrechamente conectadas a los objetos (que son instancias de
clase), no a las clases mismas. Echemos un vistazo más de cerca a ellas.

Aquí hay un ejemplo:

class ClaseEjemplo:
def __init__(self, val = 1):
self.primera = val

def setSegunda(self, val):


self.segunda = val

objetoEjemplo1 = ClaseEjemplo()
objetoEjemplo2 = ClaseEjemplo(2)

objetoEjemplo2.setSegunda(3)

objetoEjemplo3 = ClaseEjemplo(4)
objetoEjemplo3.tercera = 5

print(objetoEjemplo1.__dict__)
print(objetoEjemplo2.__dict__)
print(objetoEjemplo3.__dict__)

Se necesita una explicación adicional antes de entrar en más detalles. Echa un vistazo a las últimas
tres líneas del código.

Los objetos de Python, cuando se crean, están dotados de un pequeño conjunto de propiedades y
métodos predefinidos. Cada objeto los tiene, los quieras o no. Uno de ellos es una variable
llamada  __dict__  (es un diccionario).

La variable contiene los nombres y valores de todas las propiedades (variables) que el objeto
contiene actualmente. Vamos a usarla para presentar de forma segura el contenido de un objeto.

Vamos a sumergirnos en el código ahora:

 La clase llamada  ClaseEjemplo  tiene un constructor, el cual crea incondicionalmente


una variable de instancia llamada  primera , y le asigna el valor pasado a través del
primer argumento (desde la perspectiva del usuario de la clase) o el segundo argumento
(desde la perspectiva del constructor); ten en cuenta el valor predeterminado del parámetro:
cualquier cosa que puedas hacer con un parámetro de función regular también se puede
aplicar a los métodos.

 La clase también tiene un método que crea otra variable de instancia, llamada  segunda .

 Hemos creado tres objetos de la clase  ClaseEjemplo , pero todas estas instancias difieren:

o objetoEjemplo1  solo tiene una propiedad llamada  primera .

o objetoEjemplo2  tiene dos propiedades:  primera  y  segunda .

o objetoEjemplo3  ha sido enriquecido sobre la marcha con una propiedad


llamada  tercera , fuera del código de la clase: esto es posible y totalmente
permisible.

La salida del programa muestra claramente que nuestras suposiciones son correctas: aquí están:

{'primera': 1}
{'primera': 2, 'segunda': 3}
{'primera': 4, 'tercera': 5}

Hay una conclusión adicional que debería mencionarse aquí: el modificar una variable de
instancia de cualquier objeto no tiene impacto en todos los objetos restantes. Las variables de
instancia están perfectamente aisladas unas de otras.

Variables de instancia: continuación


Observa el ejemplo modificado en el editor.

class ClaseEjemplo:

def __init__(self, val = 1):

self.__primera = val

def setSegunda(self, val):

self.__segunda = val

objetoEjemplo1 = ClaseEjemplo()

objetoEjemplo2 = ClaseEjemplo(2)

objetoEjemplo2.setSegunda(3)
objetoEjemplo3 = ClaseEjemplo(4)

objetoEjemplo3.__tercera = 5

print(objetoEjemplo1.__dict__)

print(objetoEjemplo2.__dict__)

print(objetoEjemplo3.__dict__)

Es casi lo mismo que el anterior. La única diferencia está en los nombres de las
propiedades. Hemos agregado dos guiones bajos ( __ ) en frente de ellos.

Como sabes, tal adición hace que la variable de instancia sea privada - se vuelve
inaccesible desde el mundo exterior.

El comportamiento real de estos nombres es un poco más complicado, así que


ejecutemos el programa. Esta es la salida:

{'_ClaseEjemplo__primera': 1}
{'_ClaseEjemplo__primera': 2, '_ClaseEjemplo__segunda': 3}
{'_ClaseEjemplo__primera': 4, '__tercera': 5}

¿Puedes ver estos nombres extraños llenos de guiones bajos? ¿De dónde provienen?

Cuando Python ve que deseas agregar una variable de instancia a un objeto y lo vas a
hacer dentro de cualquiera de los métodos del objeto, maneja la operación de la
siguiente manera:

 Coloca un nombre de clase antes de tu nombre.


 Coloca un guión bajo adicional al principio.

Es por ello que  __primera  se convierte en  _ClaseEjemplo__primera .

El nombre ahora es completamente accesible desde fuera de la clase. Puedes


ejecutar un código como este:

print(objetoEjemplo1._ClaseEjemplo__primera)

y obtendrás un resultado válido sin errores ni excepciones.

Como puedes ver, hacer que una propiedad sea privada es limitado.

No funcionará si agregas una variable de instancia fuera del código de clase. En


este caso, se comportará como cualquier otra propiedad ordinaria.

Variables de Clase
Una variable de clase es una propiedad que existe en una sola copia y se almacena fuera de
cualquier objeto.

Nota: no existe una variable de instancia si no hay ningún objeto en la clase; existe una variable de
clase en una copia, incluso si no hay objetos en la clase.

Las variables de clase se crean de manera diferente. El ejemplo te dirá más:

class ClaseEjemplo:
contador = 0
def __init__(self, val = 1):
self.__primera = val
ClaseEjemplo.contador += 1

objetoEjemplo1 = ClaseEjemplo()
objetoEjemplo2 = ClaseEjemplo(2)
objetoEjemplo3 = ClaseEjemplo(4)

print(objetoEjemplo1.__dict__, objetoEjemplo1.contador)
print(objetoEjemplo2.__dict__, objetoEjemplo2.contador)
print(objetoEjemplo3.__dict__, objetoEjemplo3.contador)

Observa:

 Hay una asignación en la primera linea de la definición de clase: establece la variable


denominada  contador  a 0; inicializando la variable dentro de la clase pero fuera de
cualquiera de sus métodos hace que la variable sea una variable de clase.
 El acceder a dicha variable tiene el mismo aspecto que acceder a cualquier atributo de
instancia; está en el cuerpo del constructor; como puedes ver, el constructor incrementa la
variable en uno; en efecto, la variable cuenta todos los objetos creados.

Ejecutar el código causará el siguiente resultado:

{'_ClaseEjemplo__primera': 1} 3
{'_ClaseEjemplo__primera': 2} 3
{'_ClaseEjemplo__primera': 4} 3

Dos conclusiones importantes provienen del ejemplo:

 Las variables de clase no se muestran en el diccionario de un objeto  __dict__  (esto es


natural ya que las variables de clase no son partes de un objeto), pero siempre puedes
intentar buscar en la variable del mismo nombre, pero a nivel de clase, te mostraremos esto
muy pronto.
 Una variable de clase siempre presenta el mismo valor en todas las instancias de clase
(objetos).

Variables de Clase: continuación


Mira el ejemplo en el editor. ¿Puedes adivinar su salida?

Ejecuta el programa y verifica si tus predicciones fueron correctas. Todo funciona


como se esperaba, ¿no?

class ClaseEjemplo:
__contador = 0
def __init__(self, val = 1):
self.__primera = val
ClaseEjemplo.__contador += 1

objetoEjemplo1 = ClaseEjemplo()
objetoEjemplo2 = ClaseEjemplo(2)
objetoEjemplo3 = ClaseEjemplo(4)

print(objetoEjemplo1.__dict__, objetoEjemplo1._ClaseEjemplo__contador)
print(objetoEjemplo2.__dict__, objetoEjemplo2._ClaseEjemplo__contador)
print(objetoEjemplo3.__dict__, objetoEjemplo3._ClaseEjemplo__contador)

Resultado:

{'_ClaseEjemplo__primera': 1} 3
{'_ClaseEjemplo__primera': 2} 3
{'_ClaseEjemplo__primera': 4} 3

Variables de Clase: continuación


Hemos dicho antes que las variables de clase existen incluso cuando no se creó
ninguna instancia de clase (objeto).

Ahora aprovecharemos la oportunidad para mostrarte la diferencia entre estas dos


variables  __dict__ , la de la clase y la del objeto.

Observa el código en el editor. La prueba está ahí.

class ClaseEjemplo:

varia = 1

def __init__(self, val):

ClaseEjemplo.varia = val

print(ClaseEjemplo.__dict__)
objetoEjemplo = ClaseEjemplo(2)

print(ClaseEjemplo.__dict__)

print(objetoEjemplo.__dict__)

Echemos un vistazo más de cerca:

 Definimos una clase llamada  ClaseEjemplo .


 La clase define una variable de clase llamada  varia .
 El constructor de la clase establece la variable con el valor del parámetro.
 Nombrar la variable es el aspecto más importante del ejemplo porque:
o El cambiar la asignación a  self.varia = val  crearía una variable de instancia
con el mismo nombre que la clase.
o El cambiar la asignación a  varia = val  operaría en la variable local de un
método; (te recomendamos probar los dos casos anteriores; esto te facilitará
recordar la diferencia).
 La primera línea del código fuera de la clase imprime el valor del
atributo  ClaseEjemplo.varia . Nota: utilizamos el valor antes de instanciar el
primer objeto de la clase.

Ejecuta el código en el editor y verifica su salida.

Como puedes ver  __dict__  contiene muchos más datos que la contraparte de su
objeto. La mayoría de ellos son inútiles ahora - el que queremos que verifiques
cuidadosamente muestra el valor actual de  varia .

Nota que el  __dict__  del objeto está vacío - el objeto no tiene variables de instancia.

Comprobando la existencia de un atributo


La actitud de Python hacia la instanciación de objetos plantea una cuestión
importante: en contraste con otros lenguajes de programación, es posible que no
esperes que todos los objetos de la misma clase tengan los mismos conjuntos de
propiedades.

Justo como en el ejemplo en el editor. Míralo cuidadosamente.

class ClaseEjemplo:

def __init__(self, val):

if val % 2 != 0:

self.a = 1

else:

self.b = 1
objetoEjemplo = ClaseEjemplo(1)

print(objetoEjemplo.a)

print(objetoEjemplo.b)

El objeto creado por el constructor solo puede tener uno de los dos atributos
posibles:  a  o  b .

La ejecución del código producirá el siguiente resultado:

Traceback (most recent call last):

File ".main.py", line 11, in

print(objetoEjemplo.b)

AttributeError: 'ClaseEjemplo' object has no attribute 'b'

Como puedes ver, acceder a un atributo de objeto (clase) no existente provoca una
excepción AttributeError.

Comprobando la existencia de un atributo:


continuación
La instrucción try-except te brinda la oportunidad de evitar problemas con
propiedades inexistentes.

Es fácil: mira el código en el editor.

class ClaseEjemplo:

def __init__(self, val):

if val % 2 != 0:

self.a = 1

else:

self.b = 1

objetoEjemplo = ClaseEjemplo(1)

print(objetoEjemplo.a)
try:

print(objetoEjemplo.b)

except AttributeError:

pass

Como puedes ver, esta acción no es muy sofisticada. Esencialmente, acabamos de


barrer el tema debajo de la alfombra.

Afortunadamente, hay una forma más de hacer frente al problema.

Python proporciona una función que puede verificar con seguridad si algún


objeto / clase contiene una propiedad específica. La función se llama  hasattr , y
espera que le pasen dos argumentos:

 La clase o el objeto que se verifica.


 El nombre de la propiedad cuya existencia se debe informar (Nota: debe ser
una cadena que contenga el nombre del atributo).

La función retorna True o False.

Así es como puedes utilizarla:

class ClaseEjemplo:
def __init__(self, val):
if val % 2 != 0:
self.a = 1
else:
self.b = 1

objetoEjemplo = ClaseEjemplo(1)
print(objetoEjemplo.a)

if hasattr(objetoEjemplo, 'b'):
print(objetoEjemplo.b)

Comprobando la existencia de un atributo:


continuación
No olvides que la función  hasattr()  también puede operar en clases. Puedes
usarlo para averiguar si una variable de clase está disponible, como en el ejemplo
en el editor.
La función devuelve True si la clase especificada contiene un atributo dado,
y False de lo contrario.

¿Puedes adivinar la salida del código? Ejecútalo para verificar tus conjeturas.

class ClaseEjemplo:

attr = 1

print(hasattr(ClaseEjemplo, 'attr'))

print(hasattr(ClaseEjemplo, 'prop'))

Un ejemplo más: analiza el código a continuación e intenta predecir su salida:

class ClaseEjemplo:

a = 1

def __init__(self):

self.b = 2

objetoEjemplo = ClaseEjemplo()

print(hasattr(objetoEjemplo, 'b'))

print(hasattr(objetoEjemplo, 'a'))

print(hasattr(ClaseEjemplo, 'b'))

print(hasattr(ClaseEjemplo, 'a'))

¿Tuviste éxito? Ejecuta el código para verificar tus predicciones.

True
True
False
True

Bien, hemos llegado al final de esta sección. En la siguiente sección vamos a hablar
sobre los métodos, ya que los métodos dirigen los objetos y los activan.

 6.1.4.1 POO: Métodos


Métodos a detalle
Resumamos todos los hechos relacionados con el uso de métodos en las clases de
Python.

Como ya sabes, un método es una función que está dentro de una clase.

Hay un requisito fundamental: un método está obligado a tener al menos un


parámetro (no existen métodos sin parámetros; un método puede invocarse sin un
argumento, pero no puede declararse sin parámetros).

El primer (o único) parámetro generalmente se denomina  self . Te sugerimos que lo


sigas nombrando de esta manera, darle otros nombres puede causar sorpresas
inesperadas.

El nombre self sugiere el propósito del parámetro - identifica el objeto para el cual


se invoca el método.

Si vas a invocar un método, no debes pasar el argumento para el parámetro  self  -


Python lo configurará por ti.

El ejemplo en el editor muestra la diferencia.

class conClase:

def metodo(self):

print("método")

obj = conClase()

obj.metodo()

El código da como salida:

método

Toma en cuenta la forma en que hemos creado el objeto - hemos tratado el nombre


de la clase como una función, y devuelve un objeto recién instanciado de la clase.

Si deseas que el método acepte parámetros distintos a  self , debes:

 Colocarlos después de  self  en la definición del método.


 Pasarlos como argumentos durante la invocación sin especificar  self .
Justo como aqui:

class conClase:
def metodo(self, par):
print("método:", par)

obj = conClase()
obj.metodo(1)
obj.metodo(2)
obj.metodo(3)

El código da como salida:

método: 1
método: 2
método: 3

Métodos a detalle: continuación


El parámetro  self  es usado para obtener acceso a la instancia del objeto y las
variables de clase.

El ejemplo muestra ambas formas de utilizar el parámetro  self :

class conClase:

varia = 2

def metodo(self):

print(self.varia, self.var)

obj = conClase()

obj.var = 3

obj.metodo()

El código da como salida:

2 3
El parámetro  self  también se usa para invocar otros métodos desde dentro de la
clase.

Justo como aquí:

class conClase():

def otro(self):

print("otro")

def metodo(self):

print("método")

self.otro()

obj = conClase()

obj.metodo()

El código da como salida:

método

otro

Métodos a detalle: continuación


Si se nombra un método de esta manera:  __init__ , no será un método regular, será
un constructor.

Si una clase tiene un constructor, este se invoca automática e implícitamente cuando


se instancia el objeto de la clase.

El constructor:

 Esta obligado a tener el parámetro  self  (se configura automáticamente).


 Pudiera (pero no necesariamente) tener mas parámetros que solo  self ; si
esto sucede, la forma en que se usa el nombre de la clase para crear el objeto
debe tener la definición  __init__ .
 Se puede utilizar para configurar el objeto, es decir, inicializa
adecuadamente su estado interno, crea variables de instancia, crea instancias
de cualquier otro objeto si es necesario, etc.
Observa el código en el editor. El ejemplo muestra un constructor muy simple pero
funcional.

class conClase:

def __init__(self, valor):

self.var = valor

obj1 = conClase("objeto")

print(obj1.var)

Ejecutalo. El código da como salida:

objeto

Ten en cuenta que el constructor:

 No puede retornar un valor, ya que está diseñado para devolver un objeto


recién creado y nada más.
 No se puede invocar directamente desde el objeto o desde dentro de la
clase (puedes invocar un constructor desde cualquiera de las superclases del
objeto, pero discutiremos esto más adelante).

Métodos a detalle: continuación


Como  __init__  es un método, y un método es una función, puedes hacer los mismos
trucos con constructores y métodos que con las funciones ordinarias.

El ejemplo en el editor muestra cómo definir un constructor con un valor de


argumento predeterminado. Pruébalo.

class conClase:

def __init__(self, valor = None):

self.var = valor

obj1 = conClase("objeto")

obj2 = conClase()

print(obj1.var)

print(obj2.var)
El código da como salida:

objeto

None

Todo lo que hemos dicho sobre el manejo de los nombres también se aplica a los
nombres de métodos, un método cuyo nombre comienza con  __  está (parcialmente)
oculto.

El ejemplo muestra este efecto:

class conClase:

def visible(self):

print("visible")

def __oculto(self):

print("oculto")

obj = conClase()

obj.visible()

try:

obj.__oculto()

except:

print("fallido")

obj._conClase__oculto()

El código da como salida:

visible

fallido

oculto

La vida interna de clases y objetos


Cada clase de Python y cada objeto de Python está pre-equipado con un conjunto de
atributos útiles que pueden usarse para examinar sus capacidades.

Ya conoces uno de estos: es la propiedad  __dict__ .

Observemos cómo esta propiedad trata con los métodos: mira el código en el editor.

class conClase:

varia = 1

def __init__(self):

self.var = 2

def metodo(self):

pass

def __oculto(self):

pass

obj = conClase()

print(obj.__dict__)

print(conClase.__dict__)

Ejecútalo para ver qué produce. Verifica el resultado.

Encuentra todos los métodos y atributos definidos. Localiza el contexto en el que


existen: dentro del objeto o dentro de la clase.

La vida interna de clases y objetos: continuación


__dict__  es un diccionario. Otra propiedad incorporada que vale la pena mencionar
es una cadena llamada  __name__ .

La propiedad contiene el nombre de la clase. No es nada emocionante, es solo una


cadena.

Nota: el atributo  __name__  está ausente del objeto - existe solo dentro de las clases.
Si deseas encontrar la clase de un objeto en particular, puedes usar una función
llamada  type() , la cual es capaz (entre otras cosas) de encontrar una clase que se
haya utilizado para crear instancias de cualquier objeto.

Mira el código en el editor, ejecútalo y compruébalo tu mismo.

class conClase:

pass

print(conClase.__name__)

obj = conClase()

print(type(obj).__name__)

La salida del código es:

conClase

conClase

Nota: algo como esto  print(obj.__name__)  causará un error.

La vida interna de clases y objetos: continuación


__module__  es una cadena, también almacena el nombre del módulo que contiene
la definición de la clase.

Vamos a comprobarlo: ejecuta el código en el editor.

class conClase:

pass

print(conClase.__module__)

obj = conClase()

print(obj.__module__)

La salida del código es:

__main__

__main__
Como sabes, cualquier módulo llamado  __main__  en realidad no es un módulo, sino
es el archivo actualmente en ejecución.

La vida interna de clases y objetos: continuación


__bases__  es una tupla. La tupla contiene clases (no nombres de clases) que son
superclases directas para la clase.

El orden es el mismo que el utilizado dentro de la definición de clase.

Te mostraremos solo un ejemplo muy básico, ya que queremos resaltar como


funciona la herencia.

Además, te mostraremos cómo usar este atributo cuando discutamos los aspectos
orientados a objetos de las excepciones.

Nota: solo las clases tienen este atributo - los objetos no.

Hemos definido una función llamada  printBases() , diseñada para presentar


claramente el contenido de la tupla.

Observa el código en el editor.

class SuperUno:

pass

class SuperDos:

pass

class Sub(SuperUno, SuperDos):

pass

def printBases(cls):

print('( ', end='')

for x in cls.__bases__:

print(x.__name__, end=' ')

print(')')

printBases(SuperUno)

printBases(SuperDos)
printBases(Sub)

Ejecútalo. Su salida es:

( object )

( object )

( SuperUno SuperDos )

Nota: una clase sin superclases explícitas apunta al objeto (una clase de Python


predefinida) como su antecesor directo.

Reflexión e introspección
Todo esto permite que el programador de Python realice dos actividades importantes específicas
para muchos lenguajes objetivos. Las cuales son:

 Introspección, que es la capacidad de un programa para examinar el tipo o las propiedades


de un objeto en tiempo de ejecución.
 Reflexión, que va un paso más allá, y es la capacidad de un programa para manipular los
valores, propiedades y/o funciones de un objeto en tiempo de ejecución.

En otras palabras, no tienes que conocer la definición completa de clase/objeto para manipular el
objeto, ya que el objeto y/o su clase contienen los metadatos que te permiten reconocer sus
características durante la ejecución del programa.

Investigando Clases
¿Qué puedes descubrir acerca de las clases en Python? La respuesta es simple: todo.

Tanto la reflexión como la introspección permiten al programador hacer cualquier


cosa con cada objeto, sin importar de dónde provenga.

Analiza el código en el editor.


class MiClase:

pass

obj = MiClase()

obj.a = 1

obj.b = 2

obj.i = 3

obj.ireal = 3.5

obj.entero = 4

obj.z = 5

def incIntsI(obj):

for name in obj.__dict__.keys():

if name.startswith('i'):

val = getattr(obj, name)

if isinstance(val, int):

setattr(obj, name, val + 1)

print(obj.__dict__)

incIntsI(obj)

print(obj.__dict__)

La función llamada  incIntsI()  obtiene un objeto de cualquier clase, escanea su


contenido para encontrar todos los atributos enteros con nombres que comienzan
con i, y los incrementa en uno.

¿Imposible? ¡De ningúna manera!

Así es como funciona:

 La línea 1: define una clase simple...


 Líneas 3 a la 10: ... la llenan con algunos atributos.
 Línea 12: ¡esta es nuestra función!
 Línea 13: escanea el atributo  __dict__ , buscando todos los nombres de
atributos.
 Línea 14: si un nombre comienza con i...
 Línea 15: ... utiliza la función  getattr()  para obtener su valor actual;
nota:  getattr()  toma dos argumentos: un objeto y su nombre de propiedad
(como una cadena) y devuelve el valor del atributo actual.
 Línea 16: comprueba si el valor es de tipo entero, emplea la
función  isinstance()  para este propósito (discutiremos esto más adelante).
 Línea 17: si la comprobación sale bien, incrementa el valor de la propiedad
haciendo uso de la función  setattr() ; la función toma tres argumentos: un
objeto, el nombre de la propiedad (como una cadena) y el nuevo valor de la
propiedad.

El código da como salida:

{'a': 1, 'b': 2, 'i': 3, 'ireal': 3.5, 'entero': 4, 'z': 5}


{'a': 1, 'b': 2, 'i': 4, 'ireal': 3.5, 'entero': 4, 'z': 5}

¡Eso es todo!

Herencia: ¿por qué y cómo?


Antes de comenzar a hablar sobre la herencia, queremos presentar un nuevo y
práctico mecanismo utilizado por las clases y los objetos de Python: es la forma en
que el objeto puede presentarse a si mismo.

Comencemos con un ejemplo. Observa el código en el editor.

class Estrella:

def __init__(self, nombre, galaxia):

self.nombre = nombre

self.galaxia = galaxia

sol = Estrella("Sol", "Vía Láctea")

print(sol)

El programa imprime solo una línea de texto, que en nuestro caso es:

<__main__.Estrella object at 0x7f377e552160>

Si ejecutas el mismo código en tu computadora, verás algo muy similar, aunque el


número hexadecimal (la subcadena que comienza con 0x) será diferente, ya que es
solo un identificador de objeto interno utilizado por Python, y es poco probable que
aparezca igual cuando se ejecuta el mismo código en un entorno diferente.
Como puedes ver, la impresión aquí no es realmente útil, y algo más específico, es
preferible.

Afortunadamente, Python ofrece tal función.

Herencia: ¿por qué y cómo?


Cuando Python necesita que alguna clase u objeto deba ser presentado como una
cadena (es recomendable colocar el objeto como argumento en la invocación de la
función  print() ), intenta invocar un método llamado  __str__()  del objeto y
emplear la cadena que devuelve.

El método por default  __str__()  devuelve la cadena anterior: fea y poco informativa.
Puedes cambiarlo definiendo tu propio método del nombre.

Lo acabamos de hacer: observa el código en el editor.

class Estrella:

def __init__(self, nombre, galaxia):

self.nombre = nombre

self.galaxia = galaxia

def __str__(self):

return self.nombre + ' en la ' + self.galaxia

sol = Estrella("Sol", "Vía Láctea")

print(sol)

El método nuevo  __str__()  genera una cadena que consiste en los nombres de la
estrella y la galaxia, nada especial, pero los resultados de impresión se ven mejor
ahora, ¿no?

¿Puedes adivinar la salida? Ejecuta el código para verificar si tenías razón.

Sol en la Vía Láctea

Herencia: ¿por qué y cómo?


El término herencia es más antiguo que la programación de computadoras, y describe la práctica
común de pasar diferentes bienes de una persona a otra después de la muerte de esa persona. El
término, cuando se relaciona con la programación de computadoras, tiene un significado
completamente diferente.

Definamos el término para nuestros propósitos:

La herencia es una práctica común (en la programación de objetos) de pasar atributos y métodos
de la superclase (definida y existente) a una clase recién creada, llamada subclase.

En otras palabras, la herencia es una forma de construir una nueva clase, no desde cero, sino
utilizando un repertorio de rasgos ya definido. La nueva clase hereda (y esta es la clave) todo el
equipamiento ya existente, pero puedes agregar algo nuevo si es necesario.

Gracias a eso, es posible construir clases más especializadas (más concretas) utilizando algunos


conjuntos de reglas y comportamientos generales predefinidos.

El factor más importante del proceso es la relación entre la superclase y todas sus subclases (nota:
si B es una subclase de A y C es una subclase de B, esto también significa que C es una subclase
de A, ya que la relación es totalmente transitiva).

Aquí se presenta un ejemplo muy simple de herencia de dos niveles:

class Vehiculo:
pass

class VehiculoTerrestre(Vehiculo):
pass

class VehiculoOruga(VehiculoTerrestre):
pass

Todas las clases presentadas están vacías por ahora, ya que te mostraremos cómo funcionan las
relaciones mutuas entre las superclases y las subclases. Las llenaremos con contenido pronto.

Podemos decir que:

 La clase  Vehiculo  es la superclase para


clases  VehiculoTerrestre  y  VehiculoOruga .
 La clase  VehiculoTerrestre  es una subclase de  Vehiculo  y la superclase
de  VehiculoOruga  al mismo tiempo.
 La clase  VehiculoOruga  es una subclase tanto de  Vehiculo  y  VehiculoTerrestre .

El conocimiento anterior proviene de la lectura del código (en otras palabras, lo sabemos porque
podemos verlo).

¿Python sabe lo mismo? ¿Es posible preguntarle a Python al respecto? Sí lo es.

Herencia: issubclass()
Python ofrece una función que es capaz de identificar una relación entre dos clases,
y aunque su diagnóstico no es complejo, puede verificar si una clase particular es
una subclase de cualquier otra clase.

Así es como se ve:

issubclass(ClaseUno, ClaseDos)

La función devuelve True si  ClaseUno  es una subclase de  ClaseDos , y False de lo


contrario.

Vamos a verlo en acción, puede sorprenderte. Mira el código en el editor. Léelo


cuidadosamente.

class Vehiculo:

pass

class VehiculoTerrestre(Vehiculo):

pass

class VehiculoOruga(VehiculoTerrestre):
pass

for cls1 in [Vehiculo, VehiculoTerrestre, VehiculoOruga]:

for cls2 in [Vehiculo, VehiculoTerrestre, VehiculoOruga]:

print(issubclass(cls1, cls2), end="\t")

print()

Hay dos bucles anidados. Su propósito es verificar todos los pares de clases
ordenadas posibles y que imprima los resultados de la verificación para
determinar si el par coincide con la relación subclase-superclase.

Ejecuta el código. El programa produce el siguiente resultado:

True False False

True True False

True True True

Hagamos que el resultado sea más legible:

↓ es una subclase de → Vehiculo VehiculoTerrestre VehiculoOruga

Vehiculo True False False

VehiculoTerrestre True True False

VehiculoOruga True True True

Existe una observación importante que hacer: cada clase se considera una subclase
de sí misma.

Herencia: isinstance()
Como ya sabes, un objeto es la encarnación de una clase. Esto significa que el
objeto es como un pastel horneado usando una receta que se incluye dentro de la
clase.

Esto puede generar algunos problemas.

Supongamos que tienes un pastel (por ejemplo, resultado de un argumento pasado a


tu función). Deseas saber qué receta se ha utilizado para prepararlo. ¿Por qué? Porque
deseas saber qué esperar de él, por ejemplo, si contiene nueces o no, lo cual es
información crucial para ciertas personas.
Del mismo modo, puede ser crucial si el objeto tiene (o no tiene) ciertas características.
En otras palabras, si es un objeto de cierta clase o no.

Tal hecho podría ser detectado por la función llamada  isinstance() :

isinstance(nombreObjeto, nombreClase)

La función devuelve True si el objeto es una instancia de la clase, o False de lo


contrario.

Ser una instancia de una clase significa que el objeto (el pastel) se ha preparado
utilizando una receta contenida en la clase o en una de sus superclases.

No lo olvides: si una subclase contiene al menos las mismas caracteristicas que


cualquiera de sus superclases, significa que los objetos de la subclase pueden hacer lo
mismo que los objetos derivados de la superclase, por lo tanto, es una instancia de su
clase de inicio y cualquiera de sus superclases.

Probémoslo. Analiza el código en el editor.

class Vehiculo:

pass

class VehiculoTerrestre(Vehiculo):

pass

class VehiculoOruga(VehiculoTerrestre):

pass

miVehiculo = Vehiculo()

miVehiculoTerrestre = VehiculoTerrestre()

miVehiculoOruga = VehiculoOruga()

for obj in [miVehiculo, miVehiculoTerrestre, miVehiculoOruga]:

for cls in [Vehiculo, VehiculoTerrestre, VehiculoOruga]:

print(isinstance(obj, cls), end="\t")

print()
Hemos creado tres objetos, uno para cada una de las clases. Luego, usando dos bucles
anidados, verificamos todos los pares posibles de clase de objeto para averiguar si
los objetos son instancias de las clases.

Ejecuta el código.

Esto es lo que obtenemos:

True False False

True True False

True True True

Hagamos que el resultado sea más legible:

↓ es una instancia de → Vehiculo miVehiculoTerrestre VehiculoOruga

miVehiculo True False False

miVehiculoTerrestre True True False

VehiculoOruga True True True

¿La tabla confirma nuestras expectativas?

Herencia: el operador is
También existe un operador de Python que vale la pena mencionar, ya que se refiere
directamente a los objetos: aquí está:

objetoUno is objetoDos

El operador  is  verifica si dos variables (en este caso  objetoUno  y  objetoDos ) se
refieren al mismo objeto.

No olvides que las variables no almacenan los objetos en sí, sino solo los
identificadores que apuntan a la memoria interna de Python.

Asignar un valor de una variable de objeto a otra variable no copia el objeto, sino solo
su identificador. Es por ello que un operador como  is  puede ser muy útil en ciertas
circunstancias.

Echa un vistazo al código en el editor. Analicémoslo:

class ClaseMuestra:
def __init__(self, val):

self.val = val

ob1 = ClaseMuestra(0)

ob2 = ClaseMuestra(2)

ob3 = ob1

ob3.val += 1

print(ob1 is ob2)

print(ob2 is ob3)

print(ob3 is ob1)

print(ob1.val, ob2.val, ob3.val)

str1 = "Mary tenía un "

str2 = "Mary tenía un corderito"

str1 += "corderito"

print(str1 == str2, str1 is str2)

 Existe una clase muy simple equipada con un constructor simple, que crea una
sola propiedad. La clase se usa para instanciar dos objetos. El primero se
asigna a otra variable, y su propiedad  val  se incrementa en uno.
 Luego, el operador  is  se aplica tres veces para verificar todos los pares de
objetos posibles, y todos los valores de la propiedad  val  son mostrados en
pantalla.
 La última parte del código lleva a cabo otro experimento. Después de tres
tareas, ambas cadenas contienen los mismos textos, pero estos textos se
almacenan en diferentes objetos.

El código imprime:

False
False
True
1 2 1
True False

Los resultados prueban que  ob1  y  ob3  son en realidad los mismos objetos, mientras
que  str1  y  str2  no lo son, a pesar de que su contenido sea el mismo.
Cómo Python encuentra propiedades y métodos
Ahora veremos cómo Python trata con los métodos de herencia.

Echa un vistazo al ejemplo en el editor. Vamos a analizarlo:

class Super:

def __init__(self, nombre):

self.nombre = nombre

def __str__(self):

return "Mi nombre es " + self.nombre + "."

class Sub(Super):

def __init__(self, nombre):

Super.__init__(self, nombre)

obj = Sub("Andy")

print(obj)

 Existe una clase llamada  Super , que define su propio constructor utilizado para
asignar la propiedad del objeto, llamada  nombre .
 La clase también define el método  __str__() , lo que permite que la clase
pueda presentar su identidad en forma de texto.
 La clase se usa luego como base para crear una subclase llamada Sub . La
clase  Sub  define su propio constructor, que invoca el de la superclase. Toma
nota de cómo lo hemos hecho:  Super.__init__(self, nombre) .
 Hemos nombrado explícitamente la superclase y hemos apuntado al método
para invocar a  __init__() , proporcionando todos los argumentos necesarios.
 Hemos instanciado un objeto de la clase  Sub  y lo hemos impreso.

El código da como salida:

Mi nombre es Andy.
Nota: Como no existe el método  __str__()  dentro de la clase  Sub , la cadena a
imprimir se producirá dentro de la clase  Super . Esto significa que el
método  __str__()  ha sido heredado por la clase  Sub .

Cómo Python encuentra propiedades y métodos:


continuación
Mira el código en el editor. Lo hemos modificado para mostrarte otro método de
acceso a cualquier entidad definida dentro de la superclase.

En el ejemplo anterior, nombramos explícitamente la superclase. En este ejemplo,


hacemos uso de la función  super() , la cual accede a la superclase sin necesidad de
conocer su nombre:

super().__init__(nombre)

La función  super()  crea un contexto en el que no tiene que (además, no debe) pasar
el argumento propio al método que se invoca; es por eso que es posible activar el
constructor de la superclase utilizando solo un argumento.

Nota: puedes usar este mecanismo no solo para invocar al constructor de la


superclase, pero también para obtener acceso a cualquiera de los recursos
disponibles dentro de la superclase.

class Super:
def __init__(self, nombre):
self.nombre = nombre
def __str__(self):
return "Mi nombre es " + self.nombre + "."
class Sub(Super):
def __init__(self, nombre):
super().__init__(nombre)
obj = Sub("Andy")
print(obj)

Resultado:
Mi nombre es Andy.
Cómo Python encuentra propiedades y métodos:
continuación
Intentemos hacer algo similar, pero con propiedades (más precisamente
con: variables de clase).

Observa el ejemplo en el editor.

# Probando propiedades: variables de clase

class Super:

supVar = 1

class Sub(Super):

subVar = 2

obj = Sub()

print(obj.subVar)

print(obj.supVar)

Como puedes observar, la clase  Super  define una variable de clase llamada  supVar , y
la clase  Sub  define una variable llamada  subVar .

Ambas variables son visibles dentro del objeto de clase  Sub  - es por ello que el código
da como salida:

Cómo Python encuentra propiedades y métodos:


continuación
El mismo efecto se puede observar con variables de instancia - observa el segundo
ejemplo en el editor.

# Probando propiedades: variables de instancia

class Super:

def __init__(self):

self.supVar = 11
class Sub(Super):

def __init__(self):

super().__init__()

self.subVar = 12

obj = Sub()

print(obj.subVar)

print(obj.supVar)

El constructor de la clase  Sub  crea una variable de instancia llamada  subVar , mientras
que el constructor de  Super  hace lo mismo con una variable de nombre  supVar . Al
igual que el ejemplo anterior, ambas variables son accesibles desde el objeto de
clase  Sub .

La salida del programa es:

12

11

Nota: La existencia de la variable  supVar  obviamente está condicionada por la


invocación del constructor de la clase  Super . Omitirlo daría como resultado la
ausencia de la variable en el objeto creado (pruébalo tu mismo).

Cómo Python encuentra propiedades y métodos:


continuación
Ahora es posible formular una declaración general que describa el comportamiento de
Python.

Cuando intentes acceder a una entidad de cualquier objeto, Python intentará (en este
orden):

 Encontrarla dentro del objeto mismo.


 Encontrarla en todas las clases involucradas en la línea de herencia del objeto
de abajo hacia arriba.

Si ambos intentos fallan, una excepción ( AttributeError ) será lanzada.


La primera condición puede necesitar atención adicional. Como sabes, todos los
objetos derivados de una clase en particular pueden tener diferentes conjuntos de
atributos, y algunos de los atributos pueden agregarse al objeto mucho tiempo
después de la creación del objeto.

El ejemplo en el editor resume esto en una línea de herencia de tres niveles.


Analízalo cuidadosamente.

class Nivel1:

varia1 = 100

def __init__(self):

self.var1 = 101

def fun1(self):

return 102

class Nivel2(Nivel1):

varia2 = 200

def __init__(self):

super().__init__()

self.var2 = 201

def fun2(self):

return 202

class Nivel3(Nivel2):

varia3 = 300

def __init__(self):

super().__init__()

self.var3 = 301

def fun3(self):

return 302

obj = Nivel3()
print(obj.varia1, obj.var1, obj.fun1())

print(obj.varia2, obj.var2, obj.fun2())

print(obj.varia3, obj.var3, obj.fun3())

Resultado:

100 101 102


200 201 202
300 301 302

Todos los comentarios que hemos hecho hasta ahora están relacionados con casos de
herencia única, cuando una subclase tiene exactamente una superclase. Esta es la
situación más común (y también la recomendada).

Python, sin embargo, ofrece mucho más aquí. En las próximas lecciones te
mostraremos algunos ejemplos de herencia múltiple.

Cómo Python encuentra propiedades y métodos:


continuación
La herencia múltiple ocurre cuando una clase tiene más de una superclase.

Sintácticamente, dicha herencia se presenta como una lista de superclases separadas


por comas entre paréntesis después del nombre de la nueva clase, al igual que aquí:

class SuperA:

varA = 10

def funA(self):

return 11

class SuperB:

varB = 20

def funB(self):

return 21

class Sub(SuperA, SuperB):

pass

obj = Sub()
print(obj.varA, obj.funA())

print(obj.varB, obj.funB())

La clase  Sub  tiene dos superclases:  SuperA  y  SuperB . Esto significa que la
clase  Sub  hereda todos los bienes ofrecidos por ambas clases  SuperA  y  SuperB .

El código imprime:

10 11

20 21

Ahora es el momento de introducir un nuevo término - overriding (anulación).

¿Qué crees que sucederá si más de una de las superclases define una entidad con un
nombre en particular?

Cómo Python encuentra propiedades y métodos:


continuación
Analicemos el ejemplo en el editor.

class Nivel1:

var = 100

def fun(self):

return 101

class Nivel2:

var = 200

def fun(self):

return 201

class Nivel3(Nivel2):

pass

obj = Nivel3()
print(obj.var, obj.fun())

Tanto la clase  Nivel1  como la  Nivel2  definen un método llamado  fun()  y una
propiedad llamada  var . ¿Significará esto el objeto de la clase  Nivel3  podrá acceder a
dos copias de cada entidad? De ningún modo.

La entidad definida después (en el sentido de herencia) anula la misma entidad


definida anteriormente. Es por eso que el código produce el siguiente resultado:

200 201

Como puedes ver, la variable de clase  var  y el método  fun()  de la clase  Nivel2  anula
las entidades de los mismos nombres derivados de la clase  Nivel1 .

Esta característica se puede usar intencionalmente para modificar el comportamiento


predeterminado de las clases (o definido previamente) cuando cualquiera de tus
clases necesite actuar de manera diferente a su ancestro.

También podemos decir que Python busca una entidad de abajo hacia arriba, y
está completamente satisfecho con la primera entidad del nombre deseado que
encuentre.

¿Qué ocurre cuando una clase tiene dos ancestros que ofrecen la misma entidad y se
encuentran en el mismo nivel? En otras palabras, ¿Qué se debe esperar cuando surge
una clase usando herencia múltiple? Miremos lo siguiente.

Cómo Python encuentra propiedades y métodos:


continuación
Echemos un vistazo al ejemplo en el editor.

class Izquierda:

var = "I"

varIzquierda = "II"

def fun(self):

return "Izquierda"

class Derecha:

var = "D"

varDerecha = "DD"
def fun(self):

return "Derecha"

class Sub(Izquierda, Derecha):

pass

obj = Sub()

print(obj.var, obj.varIzquierda, obj.varDerecha, obj.fun())

La clase  Sub  hereda todos los bienes de dos superclases,  Izquierda  y  Derecha  (estos
nombres están destinados a ser significativos).

No hay duda de que la variable de clase  varDerecha  proviene de la clase  Derecha , y


la variable  varIzquierda  proviene de la clase  Izquierda  respectivamente.

Esto es claro. Pero, ¿De donde proviene la variable  var ? ¿Es posible adivinarlo? El
mismo problema se encuentra con el método  fun()  - ¿Será invocado
desde  Izquierda  o desde  Derecha ? Ejecutemos el programa: la salida será:

I II DD Izquierda

Esto prueba que ambos casos poco claros tienen una solución dentro de la
clase  Izquierda . ¿Es esta una premisa suficiente para formular una regla general? Sí
lo es.

Podemos decir que Python busca componentes de objetos en el siguiente orden:

 Dentro del objeto mismo.


 En sus superclases, de abajo hacia arriba.
 Si hay más de una clase en una ruta de herencia, Python las escanea de
izquierda a derecha.

¿Necesitas algo más? Simplemente haz una pequeña enmienda en el código -


reemplaza: class Sub(Izquierda, Derecha):  con:  class Sub(Derecha,
Izquierda): , luego ejecuta el programa nuevamente y observa qué sucede.

¿Qué ves ahora? Vemos:

D II DD Derecha

¿Ves lo mismo o algo diferente?

Cómo construir una jerarquía de clases


Construir una jerarquía de clases no es solo por amor al arte.

Si divides un problema entre las clases y decides cuál de ellas debe ubicarse en la
parte superior y cuál debe ubicarse en la parte inferior de la jerarquía, debes analizar
cuidadosamente el problema, pero antes de mostrarte cómo hacerlo (y cómo no
hacerlo), queremos resaltar un efecto interesante. No es nada extraordinario (es solo
una consecuencia de las reglas generales presentadas anteriormente), pero recordarlo
puede ser clave para comprender cómo funcionan algunos códigos y cómo se puede
usar este efecto para construir un conjunto flexible de clases.

Echa un vistazo al código en el editor. Analicémoslo:

class Uno:

def hazlo(self):

print("hazlo de Uno")

def haz_algo(self):

self.hazlo()

class Dos(Uno):

def hazlo(self):

print("hazlo de Dos")

uno = Uno()

dos = Dos()

uno.haz_algo()

dos.haz_algo()

 Existen dos clases llamadas  Uno  y  Dos , se entiende que  Dos  es derivada
de  Uno . Nada especial. Sin embargo, algo es notable: el método  doit() .
 El método  doit()  está definido dos veces: originalmente dentro de  Uno  y
posteriormente dentro de  Dos . La esencia del ejemplo radica en el hecho de
que es invocado solo una vez - dentro de  Uno .

La pregunta es: ¿cuál de los dos métodos será invocado por las dos últimas líneas del
código?

La primera invocación parece ser simple, el invocar el método  haz_algo()  del


objeto  uno  obviamente activará el primero de los métodos.
La segunda invocación necesita algo de atención. También es simple si tienes en
cuenta cómo Python encuentra los componentes de la clase. La segunda invocación
lanzará el método  hazlo()  en la forma existente dentro de la clase  Dos ,
independientemente del hecho de que la invocación se lleva a cabo dentro de la
clase  Uno .

En efecto, el código genera el siguiente resultado:

hazlo de Uno
hazlo de Dos

Nota: la situación en la cual la subclase puede modificar el comportamiento de su


superclase (como en el ejemplo) se llama polimorfismo. La palabra proviene del
griego (polys: "muchos, mucho" y morphe, "forma, forma"), lo que significa que una
misma clase puede tomar varias formas dependiendo de las redefiniciones realizadas
por cualquiera de sus subclases.

El método, redefinido en cualquiera de las superclases, que cambia el


comportamiento de la superclase, se llama virtual.

En otras palabras, ninguna clase se da por hecho. El comportamiento de cada clase


puede ser modificado en cualquier momento por cualquiera de sus subclases.

Te mostraremos cómo usar el polimorfismo para extender la flexibilidad de la


clase.

Cómo construir una jerarquía de clases:


continuación
Mira el ejemplo en el editor.

import time

class VehiculoOruga:

def control_de_pista(izquierda, alto):

pass

def girar(izquierda):

control_de_pista(izquierda, True)

time.sleep(0.25)

control_de_pista(izquierda, False)
class VehiculoTerrestre:

def girar_ruedas_delanteras(izquierda, on):

pass

def girar(izquierda):

girar_ruedas_delanteras(izquierda, True)

time.sleep(0.25)

girar_ruedas_delanteras(izquierda, False)

¿Se parece a algo? Sí, por supuesto que lo hace. Se refiere al ejemplo que se muestra
al comienzo del módulo cuando hablamos de los conceptos generales de la
programación orientada a objetos.

Puede parecer extraño, pero no utilizamos herencia en este ejemplo, solo queríamos
mostrarte que no nos limita.

Definimos dos clases separadas capaces de producir dos tipos diferentes de vehículos
terrestres. La principal diferencia entre ellos está en cómo giran. Un vehículo con
ruedas solo gira las ruedas delanteras (generalmente). Un vehículo oruga tiene que
detener una de las pistas.

¿Puedes seguir el código?

 Un vehículo oruga realiza un giro deteniéndose y moviéndose en una de sus


pistas (esto lo hace el método  control_de_pista() , el cual se implementará
más tarde).
 Un vehículo con ruedas gira cuando sus ruedas delanteras giran (esto lo hace el
método  girar_ruedas_delanteras() ).
 El método  girar()  utiliza el método adecuado para cada vehículo en
particular.

¿Puedes detectar el error del código?

Los métodos  girar() son muy similares como para dejarlos en esta forma.

Vamos a reconstruir el código: vamos a presentar una superclase para reunir todos los
aspectos similares de los vehículos, trasladando todos los detalles a las subclases.

Cómo construir una jerarquía de clases:


continuación
Mira el código en el editor nuevamente.
import time

class Vehiculo:

def cambiardireccion(izquierda, on):

pass

def girar(izquierda):

cambiardireccion(izquierda, True)

time.sleep(0.25)

cambiardireccion(izquierda, False)

class VehiculoOruga(Vehiculo):

def control_de_pista(izquierda, alto):

pass

def cambiardireccion(izquierda, on):

control_de_pista(izquierda, on)

class VehiculoTerrestre(Vehiculo):

def girar_ruedas_delanteras(izquierda, on):

pass

def cambiardireccion(izquierda, on):

girar_ruedas_delanteras(izquierda, on)

Esto es lo que hemos hecho:

 Definimos una superclase llamada  Vehiculo , la cual utiliza el


método  girar()  para implementar un esquema para poder girar, mientras
que el giro en si es realizado por  cambiardireccion() ; nota: dicho método
está vacío, ya que vamos a poner todos los detalles en la subclase (dicho
método a menudo se denomina método abstracto, ya que solo demuestra
alguna posibilidad que será instanciada más tarde).
 Definimos una subclase llamada  VehiculoOruga  (nota: es derivada de la
clase  Vehiculo ) la cual instancia el método  cambiardireccion()  utilizando el
método denominado  control_de_pista().
 Respectivamente, la subclase llamada  VehiculoTerrestre  hace lo mismo,
pero usa el método  girar_ruedas_delanteras()  para obligar al vehículo a
girar.

La ventaja más importante (omitiendo los problemas de legibilidad) es que esta forma
de código te permite implementar un nuevo algoritmo de giro simplemente
modificando el método  girar() , lo cual se puede hacer en un solo lugar, ya que todos
los vehículos lo obedecerán.

Así es como el el polimorfismo ayuda al desarrollador a mantener el código limpio


y consistente.

Cómo construir una jerarquía de clases:


continuación
La herencia no es la única forma de construir clases adaptables. Puedes lograr los
mismos objetivos (no siempre, pero muy a menudo) utilizando una técnica llamada
composición.

La composición es el proceso de componer un objeto usando otros objetos


diferentes. Los objetos utilizados en la composición entregan un conjunto de rasgos
deseados (propiedades y / o métodos), podemos decir que actúan como bloques
utilizados para construir una estructura más complicada.

Puede decirse que:

 La herencia extiende las capacidades de una clase agregando nuevos


componentes y modificando los existentes; en otras palabras, la receta
completa está contenida dentro de la clase misma y todos sus ancestros; el
objeto toma todas las pertenencias de la clase y las usa.
 La composición proyecta una clase como contenedor capaz de almacenar y
usar otros objetos (derivados de otras clases) donde cada uno de los objetos
implementa una parte del comportamiento de una clase.

Permítenos ilustrar la diferencia usando los vehículos previamente definidos. El


enfoque anterior nos condujo a una jerarquía de clases en la que la clase más alta
conocía las reglas generales utilizadas para girar el vehículo, pero no sabía cómo
controlar los componentes apropiados (ruedas o pistas).

Las subclases implementaron esta capacidad mediante la introducción de mecanismos


especializados. Hagamos (casi) lo mismo, pero usando composición. La clase, como en
el ejemplo anterior, sabe cómo girar el vehículo, pero el giro real lo realiza un objeto
especializado almacenado en una propiedad llamada  controlador .
El  controlador  es capaz de controlar el vehículo manipulando las partes relevantes
del vehículo.
Echa un vistazo al editor: así es como podría verse.

import time

class Pistas:

def cambiardireccion(self, izquierda, on):

print("pistas: ", izquierda, on)

class Ruedas:

def cambiardireccion(self, izquierda, on):

print("ruedas: ", izquierda, on)

class Vehiculo:

def __init__(self, controlador):

self.controlador = controlador

def girar(self, izquierda):

self.controlador.cambiardireccion(izquierda, True)

time.sleep(0.25)

self.controlador.cambiardireccion(izquierda, False)

conRuedas = Vehiculo(Ruedas())

conPistas = Vehiculo(Pistas())

conRuedas.girar(True)

conPistas.girar(False)

Existen dos clases llamadas  Pistas  y  Ruedas  - ellas saben cómo controlar la dirección
del vehículo. También hay una clase llamada  Vehiculo  que puede usar cualquiera de
los controladores disponibles (los dos ya definidos o cualquier otro definido en el
futuro): el  controlador  se pasa a la clase durante la inicialización.

De esta manera, la capacidad de giro del vehículo se compone de un objeto externo,


no implementado dentro de la clase  Vehiculo .

En otras palabras, tenemos un vehículo universal y podemos instalar pistas o ruedas


en él.
El código produce el siguiente resultado:

ruedas: True True


ruedas: True False
pistas: False True
pistas: False False

Herencia simple versus herencia múltiple


Como ya sabes, no hay obstáculos para usar la herencia múltiple en Python. Puedes derivar cualquier
clase nueva de más de una clase definida previamente.

Solo hay un "pero". El hecho de que puedas hacerlo no significa que tengas que hacerlo.

No olvides que:

 Una sola clase de herencia siempre es más simple, segura y fácil de entender y mantener.

 La herencia múltiple siempre es arriesgada, ya que tienes muchas más oportunidades de


cometer un error al identificar estas partes de las superclases que influirán efectivamente en
la nueva clase.

 La herencia múltiple puede hacer que la anulación sea extremadamente difícil; además, el
emplear la función  super()  se vuelve ambiguo.

 La herencia múltiple viola el principio de responsabilidad única (mas detalles


aquí: https://en.wikipedia.org/wiki/Single_responsibility_principle) ya que forma una nueva
clase de dos (o más) clases que no saben nada una de la otra.

 Sugerimos encarecidamente la herencia múltiple como la última de todas las posibles


soluciones: si realmente necesitas las diferentes funcionalidades que ofrecen las diferentes
clases, la composición puede ser una mejor alternativa.

Diamantes y porque no los quieres


El espectro de problemas que posiblemente provienen de la herencia múltiple se ilustra mediante un
problema clásico denominado problema de diamantes. El nombre refleja la forma del diagrama de
herencia: echa un vistazo a la imagen.

 Existe la superclase superior nombrada A.


 Aquí hay dos subclases derivadas de A - B y C.
 Y también está la subclase inferior llamada D, derivada de B y C (o C y B, ya que estas dos
variantes significan cosas diferentes en Python).

¿Puedes ver el diamante allí?


A Python, sin embargo, no le gustan los diamantes, y no te permitirá implementar algo como esto. Si
intentas construir una jerarquía como esta:

class A:

pass

class B(A):

pass

class C(A):

pass

class D(A, B):

pass

d = D()

Obtendrás una excepción TypeError, junto con el siguiente mensaje:

Cannot create a consistent method resolution

order (MRO) for bases B, A


Donde  MRO  significa Method Resolution Order. Este es el algoritmo que Python utiliza para buscar
el árbol de herencia y encontrar los métodos necesarios.

Los diamantes son preciosos y valiosos ... pero no en la programación. Evítalos por tu propio bien.

 6.1.6.1 Excepciones una vez más

Más sobre excepciones


El discutir sobre la programación orientada a objetos ofrece una muy buena
oportunidad para volver a las excepciones. La naturaleza orientada a objetos de las
excepciones de Python las convierte en una herramienta muy flexible, capaz de
adaptarse a necesidades específicas, incluso aquellas que aún no conoces.

Antes de adentrarnos en el lado orientado a objetos de las excepciones, queremos


mostrarte algunos aspectos sintácticos y semánticos de la forma en que Python trata
el bloque try-except, ya que ofrece un poco más de lo que hemos presentado hasta
ahora.

La primera característica que queremos analizar aquí es una rama adicional posible
que se puede colocar dentro (o más bien, directamente detrás) del bloque try-except:
es la parte del código que comienza con  else  - justo como el ejemplo en el editor.

Un código etiquetado de esta manera se ejecuta cuando (y solo cuando) no se ha


generado ninguna excepción dentro de la parte del  try: . Podemos decir que esta
rama se ejecuta después del  try:  - ya sea el que comienza con  except  (no olvides
que puede haber más de una rama de este tipo) o la que comienza con  else .

Nota: la rama  else:  debe ubicarse después de la última rama  except .

El código de ejemplo produce el siguiente resultado:

Todo salió bien

0.5

División fallida

None

def reciproco(n):
try:
n=1/n
except ZeroDivisionError:
print("División fallida")
return None
else:
print("Todo salió bien")
return n
print(reciproco(2))
print(reciproco(0))

Más sobre excepciones


El bloque try-except se puede extender de una manera más: agregando una parte
encabezada por la palabra clave reservada  finally  (debe ser la última rama del
código diseñada para manejar excepciones).

Nota: estas dos variantes ( else  y  finally ) no son dependientes entre si, y pueden
coexistir u ocurrir de manera independiente.

El bloque  finally  siempre se ejecuta (finaliza la ejecución del bloque try-except, de


ahí su nombre), sin importar lo que sucedió antes, incluso cuando se genera o lanza
una excepción, sin importar si esta se ha manejado o no.

Mira el código en el editor.

def reciproco(n):

try:

n=1/n

except ZeroDivisionError:

print("División fallida")

n = None

else:

print("Todo salió bien")

finally:

print("Es el momento de decir adiós")

return n

print(reciproco(2))

print(reciproco(0))
Su salida es:

Todo salió bien


Es el momento de decir adiós
0.5
División fallida
Es el momento de decir adiós
None

Las excepciones son clases


Los ejemplos anteriores se centraron en detectar un tipo específico de excepción y
responder de manera apropiada. Ahora vamos a profundizar más y mirar dentro de la
excepción misma.

Probablemente no te sorprenderá saber que las excepciones son clases. Además,


cuando se genera una excepción, se crea una instancia de un objeto de la clase y pasa
por todos los niveles de ejecución del programa, buscando la rama "except" que está
preparada para tratar con la excepción.

Tal objeto lleva información útil que puede ayudarte a identificar con precisión todos
los aspectos de la situación pendiente. Para lograr ese objetivo, Python ofrece una
variante especial de la cláusula de excepción: puedes encontrarla en el editor.

Como puedes ver, la sentencia  except  se extendió y contiene una frase adicional que
comienza con la palabra clave reservada  as , seguida por un identificador. El
identificador está diseñado para capturar la excepción con el fin de analizar su
naturaleza y sacar conclusiones adecuadas.

Nota: el alcance del identificador solo es dentro del  except , y no va más allá.

El ejemplo presenta una forma muy simple de utilizar el objeto recibido: simplemente
imprímelo (como puedes ver, la salida es producida por el método del
objeto  __str__() ) y contiene un breve mensaje que describe la razón.

Se imprimirá el mismo mensaje si no hay un bloque  except  en el código, y Python se


verá obligado a manejarlo por si mismo.

Código:
try:
i = int("Hola!")
except Exception as e:
print(e)
print(e.__str__())
Resultado:
invalid literal for int() with base 10: 'Hola!'
invalid literal for int() with base 10: 'Hola!'

Las excepciones son clases


Todas las excepciones integradas de Python forman una jerarquía de clases.

Analiza el código en el editor.

def printExcTree(thisclass, nest = 0):

if nest > 1:

print(" |" * (nest - 1), end="")

if nest > 0:

print(" +---", end="")

print(thisclass.__name__)

for subclass in thisclass.__subclasses__():

printExcTree(subclass, nest + 1)

printExcTree(BaseException)

Este programa muestra todas las clases de las excepciónes predefinidas en forma de
árbol.

Como un árbol es un ejemplo perfecto de una estructura de datos recursiva, la


recursión parece ser la mejor manera de recorrerlo. La función  printExcTree()  toma
dos argumentos:

 Un punto dentro del árbol desde el cual comenzamos a recorrerlo.


 Un nivel de anidación (lo usaremos para construir un dibujo simplificado de las
ramas del árbol).

Comencemos desde la raíz del árbol: la raíz de las clases de excepciónes de Python es
la clase  BaseException  (es una superclase de todas las demás excepciones).

Para cada una de las clases encontradas, se realiza el mismo conjunto de operaciones:

 Imprimir su nombre, tomado de la propiedad  __name__ .


 Iterar a través de la lista de subclases provistas por el
método  __subclasses__() , e invocar recursivamente la
función  printExcTree() , incrementando el nivel de anidación
respectivamente.

Ten en cuenta cómo hemos dibujado las ramas. La impresión no está ordenada de
alguna manera: si deseas un desafío, puedes intentar ordenarla tu mismo. Además,
hay algunas imprecisiones sutiles en la forma en que se presentan algunas ramas. Eso
también se puede arreglar, si lo deseas.

Así es como se ve:

BaseException

+---Exception

| +---TypeError

| +---StopAsyncIteration

| +---StopIteration

| +---ImportError

| | +---ModuleNotFoundError

| | +---ZipImportError

| +---OSError

| | +---ConnectionError

| | | +---BrokenPipeError

| | | +---ConnectionAbortedError

| | | +---ConnectionRefusedError

| | | +---ConnectionResetError

| | +---BlockingIOError

| | +---ChildProcessError

| | +---FileExistsError

| | +---FileNotFoundError

| | +---IsADirectoryError

| | +---NotADirectoryError

| | +---InterruptedError

| | +---PermissionError

| | +---ProcessLookupError

| | +---TimeoutError

| | +---UnsupportedOperation
| | +---herror

| | +---gaierror

| | +---timeout

| | +---Error

| | | +---SameFileError

| | +---SpecialFileError

| | +---ExecError

| | +---ReadError

| +---EOFError

| +---RuntimeError

| | +---RecursionError

| | +---NotImplementedError

| | +---_DeadlockError

| | +---BrokenBarrierError

| +---NameError

| | +---UnboundLocalError

| +---AttributeError

| +---SyntaxError

| | +---IndentationError

| | | +---TabError

| +---LookupError

| | +---IndexError

| | +---KeyError

| | +---CodecRegistryError

| +---ValueError

| | +---UnicodeError

| | | +---UnicodeEncodeError

| | | +---UnicodeDecodeError

| | | +---UnicodeTranslateError

| | +---UnsupportedOperation

| +---AssertionError
| +---ArithmeticError

| | +---FloatingPointError

| | +---OverflowError

| | +---ZeroDivisionError

| +---SystemError

| | +---CodecRegistryError

| +---ReferenceError

| +---BufferError

| +---MemoryError

| +---Warning

| | +---UserWarning

| | +---DeprecationWarning

| | +---PendingDeprecationWarning

| | +---SyntaxWarning

| | +---RuntimeWarning

| | +---FutureWarning

| | +---ImportWarning

| | +---UnicodeWarning

| | +---BytesWarning

| | +---ResourceWarning

| +---error

| +---Verbose

| +---Error

| +---TokenError

| +---StopTokenizing

| +---Empty

| +---Full

| +---_OptionError

| +---TclError

| +---SubprocessError

| | +---CalledProcessError
| | +---TimeoutExpired

| +---Error

| | +---NoSectionError

| | +---DuplicateSectionError

| | +---DuplicateOptionError

| | +---NoOptionError

| | +---InterpolationError

| | | +---InterpolationMissingOptionError

| | | +---InterpolationSyntaxError

| | | +---InterpolationDepthError

| | +---ParsingError

| | | +---MissingSectionHeaderError

| +---InvalidConfigType

| +---InvalidConfigSet

| +---InvalidFgBg

| +---InvalidTheme

| +---EndOfBlock

| +---BdbQuit

| +---error

| +---_Stop

| +---PickleError

| | +---PicklingError

| | +---UnpicklingError

| +---_GiveupOnSendfile

| +---error

| +---LZMAError

| +---RegistryError

| +---ErrorDuringImport

+---GeneratorExit

+---SystemExit

+---KeyboardInterrupt
Anatomía detallada de las excepciones
Echemos un vistazo más de cerca al objeto de la excepción, ya que hay algunos
elementos realmente interesantes aquí (volveremos al tema pronto cuando
consideremos las técnicas base de entrada y salida de Python, ya que su subsistema
de excepción extiende un poco estos objetos).

La clase  BaseException  introduce una propiedad llamada  args . Es una


tupla diseñada para reunir todos los argumentos pasados al constructor de la
clase. Está vacío si la construcción se ha invocado sin ningún argumento, o solo
contiene un elemento cuando el constructor recibe un argumento (no se considera el
argumento  self  aquí), y así sucesivamente.

Hemos preparado una función simple para imprimir la propiedad  args  de una
manera elegante, puedes ver la función en el editor.

def printargs(args):

lng = len(args)

if lng == 0:

print("")

elif lng == 1:

print(args[0])

else:

print(str(args))

try:

raise Exception

except Exception as e:

print(e, e.__str__(), sep=' : ' ,end=' : ')

printargs(e.args)

try:

raise Exception("mi excepción")

except Exception as e:
print(e, e.__str__(), sep=' : ', end=' : ')

printargs(e.args)

try:

raise Exception("mi", "excepción")

except Exception as e:

print(e, e.__str__(), sep=' : ', end=' : ')

printargs(e.args)

Hemos utilizado la función para imprimir el contenido de la propiedad  args  en tres
casos diferentes, donde la excepción de la clase  Exception  es lanzada de tres
maneras distintas. Para hacerlo más espectacular, también hemos impreso el objeto
en sí, junto con el resultado de la invocación  __str__() .

El primer caso parece de rutina, solo hay el nombre Exception despues de la palabra


clave reservada  raise . Esto significa que el objeto de esta clase se ha creado de la
manera más rutinaria.

El segundo y el tercer caso pueden parecer un poco extraños a primera vista, pero no
hay nada extraño, son solo las invocaciones del constructor. En la segunda
sentencia  raise , el constructor se invoca con un argumento, y en el tercero, con dos.

Como puedes ver, la salida del programa refleja esto, mostrando los contenidos
apropiados de la propiedad  args :

: :
mi excepción : mi excepción : mi excepción
('mi', 'excepción') : ('mi', 'excepción') : ('mi', 'excepción')

Cómo crear tu propia excepción


La jerarquía de excepciones no está cerrada ni terminada, y siempre puedes ampliarla
si deseas o necesitas crear tu propio mundo poblado con tus propias excepciones.

Puede ser útil cuando se crea un módulo complejo que detecta errores y genera
excepciones, y deseas que las excepciones se distingan fácilmente de cualquier otra de
Python.

Esto se puede hacer al definir tus propias excepciones como subclases derivadas
de las predefinidas.
Nota: si deseas crear una excepción que se utilizará como un caso especializado de
cualquier excepción incorporada, derivala solo de esta. Si deseas construir tu propia
jerarquía, y no quieres que esté estrechamente conectada al árbol de excepciones de
Python, derivala de cualquiera de las clases de excepción principales, tal
como: Exception.

Imagina que has creado una aritmética completamente nueva, regida por sus propias
leyes y teoremas. Está claro que la división también se ha redefinido, y tiene que
comportarse de una manera diferente a la división de rutina. También está claro que
esta nueva división debería plantear su propia excepción, diferente de la
incorporada ZeroDivisionError, pero es razonable suponer que, en algunas
circunstancias, tu (o el usuario de tu aritmética) pueden tratar todas las divisiones
entre cero de la misma manera.

Demandas como estas pueden cumplirse en la forma presentada en el editor. Mira el


código y analicémoslo:

class MyZeroDivisionError(ZeroDivisionError):

pass

def doTheDivision(mine):

if mine:

raise MyZeroDivisionError("peores noticias")

else:

raise ZeroDivisionError("malas noticias")

for mode in [False, True]:

try:

doTheDivision(mode)

except ZeroDivisionError:

print('División entre cero')

for mode in [False, True]:

try:

doTheDivision(mode)

except MyZeroDivisionError:
print('Mi división entre cero')

except ZeroDivisionError:

print('División entre cero original')

 Hemos definido nuestra propia excepción, llamada  MyZeroDivisionError ,


derivada de la incorporada  ZeroDivisionError . Como puedes ver, hemos
decidido no agregar ningún componente nuevo a la clase.

En efecto, una excepción de esta clase puede ser, dependiendo del punto de
vista deseado, tratada como una simple excepción ZeroDivisionError, o
puede ser considerada por separado.

 La función  doTheDivision()  lanza una


excepción  MyZeroDivisionError  o  ZeroDivisionError , dependiendo del
valor del argumento.

La función se invoca cuatro veces en total, mientras que las dos primeras
invocaciones se manejan utilizando solo una rama  except  (la más general), las
dos últimas invocan dos ramas diferentes, capaces de distinguir las
excepciones (no lo olvides: el orden de las ramas hace una diferencia
fundamental).

Resultado:

División entre cero


División entre cero
División entre cero original
Mi división entre cero

Cómo crear tu propia excepción: continuación


Cuando vas a construir un universo completamente nuevo lleno de criaturas
completamente nuevas que no tienen nada en común con todas las cosas familiares,
es posible que desees construir tu propia estructura de excepciones.

Por ejemplo, si trabajas en un gran sistema de simulación destinado a modelar las


actividades de un restaurante de pizza, puede ser conveniente formar una jerarquía
de excepciones por separado.

Puedes comenzar a construirla definiendo una excepción general como una nueva


clase base para cualquier otra excepción especializada. Lo hemos hecho de la
siguiente manera:

class PizzaError(Exception):
def __init__(self, pizza, mensaje):

Exception.__init__(mensaje)

self.pizza = pizza

Nota: vamos a recopilar más información específica aquí de lo que recopila


una Excepción regular, entonces nuestro constructor tomará dos argumentos:

 Uno que especifica una pizza como tema del proceso.


 Otro que contiene una descripción más o menos precisa del problema.

Como puedes ver, pasamos el segundo parámetro al constructor de la superclase y


guardamos el primero dentro de nuestra propiedad.

Un problema más específico (como un exceso de queso) puede requerir una


excepción más específica. Es posible derivar la nueva clase de la ya
definida  PizzaError , como hemos hecho aquí:

class DemasiadoQuesoError(PizzaError):

def __init__(self, pizza, queso, mensaje):

PizzaError._init__(self, pizza, mensaje)

self.queso = queso

La excepción  DemasiadoQuesoError  necesita más información que la excepción


regular  PizzaError , así que lo agregamos al constructor, el nombre  queso  es
entonces almacenado para su posterior procesamiento.

Cómo crear tu propia excepción: continuación


Mira el código en el editor. Combinamos las dos excepciones previamente definidas y
las aprovechamos para que funcionen en un pequeño ejemplo.

class PizzaError(Exception):

def __init__(self, pizza, mensaje):

Exception.__init__(self, mensaje)

self.pizza = pizza

class DemasiadoQuesoError(PizzaError):
def __init__(self, pizza, queso, mensaje):

PizzaError.__init__(self, pizza, mensaje)

self.queso = queso

def makePizza(pizza, queso):

if pizza not in ['margherita', 'capricciosa', 'calzone']:

raise PizzaError(pizza, "no hay tal pizza en el menú")

if queso > 100:

raise DemasiadoQuesoError(pizza, queso, "demasiado queso")

print("¡Pizza lista!")

for (pz, ch) in [('calzone', 0), ('margherita', 110), ('mafia', 20)]:

try:

makePizza(pz, ch)

except DemasiadoQuesoError as tmce:

print(tmce, ':', tmce.queso)

except PizzaError as pe:

print(pe, ':', pe.pizza)

Resultado:

¡Pizza lista!
demasiado queso : 110
no hay tal pizza en el menú : mafia

Una de ellas es lanzada dentro de la función  hacerPizza()  cuando ocurra cualquiera


de estas dos situaciones erróneas: una solicitud de pizza incorrecta o una solicitud de
una pizza con demasiado queso.

Nota:
 El remover la rama que comienza con  except DemasiadoQuesoError  hará
que todas las excepciones que aparecen se clasifiquen como  PizzaError .
 El remover la rama que comienza con  except PizzaError  provocará que la
excepción  DemasiadoQuesoError  no pueda ser manejada, y hará que el
programa finalice.

La solución anterior, aunque elegante y eficiente, tiene una debilidad importante.


Debido a la manera algo fácil de declarar los constructores, las nuevas excepciones no
se pueden usar tal cual, sin una lista completa de los argumentos requeridos.

Eliminaremos esta debilidad estableciendo valores predeterminados para todos


los parámetros del constructor. Observa:

class PizzaError(Exception):

def __init__(self, pizza='desconocida', mensaje=''):

Exception.__init__(self, mensaje)

self.pizza = pizza

class DemasiadoQuesoError(PizzaError):

def __init__(self, pizza='desconocida', queso='>100',


mensaje=''):

PizzaError.__init__(self, pizza, mensaje)

self.queso = queso

def hacerPizza(pizza, queso):

if pizza not in ['margherita', 'capricciosa', 'calzone']:

raise PizzaError

if queso > 100:

raise DemasiadoQuesoError

print("¡Pizza lista!")
for (pz, ch) in [('calzone', 0), ('margherita', 110), ('mafia',
20)]:

try:

hacerPizza(pz, ch)

except DemasiadoQuesoError as tmce:

print(tmce, ':', tmce.queso)

except PizzaError as pe:

print(pe, ':', pe.pizza)

Ahora, si las circunstancias lo permiten, es posible usar unicamente los nombres de


clase.

 6.1.7.1 Generadores y cierres

Generadores, dónde encontrarlos


Generador - ¿Con qué asocias esta palabra? Quizás se refiere a algún dispositivo
electrónico. O tal vez se refiere a una máquina pesada diseñada para producir energía
eléctrica u otra cosa.

Un generador de Python es un fragmento de código especializado capaz de


producir una serie de valores y controlar el proceso de iteración. Esta es la razón
por la cual los generadores a menudo se llaman iteradores, y aunque hay quienes
pueden encontrar una diferencia entre estos dos, aquí los trataremos como uno
mismo.

Puede que no te hayas dado cuenta, pero te has topado con generadores muchas,
muchas veces antes. Echa un vistazo al fragmento de código:

for i in range(5):
print(i)

Resultado:
0
1
2
3
4

La función  range()  es un generador, la cual también es un iterador.

¿Cuál es la diferencia?

Una función devuelve un valor bien definido, el cual, puede ser el resultado de una
evaluación compleja, por ejemplo, de un polinomio, y se invoca una vez, solo una vez.
Un generador devuelve una serie de valores, y en general, se invoca (implícitamente)
más de una vez.

En el ejemplo, el generador  range()  se invoca seis veces, proporcionando cinco


valores de cero a cuatro.

El proceso anterior es completamente transparente. Vamos a arrojar algo de luz sobre


el. Vamos a mostrarte el protocolo iterador.

Generadores, dónde encontrarlos: continuación


El protocolo iterador es una forma en que un objeto debe comportarse para
ajustarse a las reglas impuestas por el contexto de las sentencias  for  e  in . Un
objeto conforme al protocolo iterador se llama iterador.

Un iterador debe proporcionar dos métodos:

 __iter__()  el cual debe devolver el objeto en sí y que se invoca una vez (es
necesario para que Python inicie con éxito la iteración).
 __next__()  el cual debe devolver el siguiente valor (primero, segundo, etc.)
de la serie deseada: será invocado por las sentencias  for / in  para pasar a la
siguiente iteración; si no hay más valores a proporcionar, el método
deberá lanzar la excepción  StopIteration .

¿Suena extraño? De ningúna manera. Mira el ejemplo en el editor.

Hemos creado una clase capaz de iterar a través de los primeros  n  valores (donde  n  es
un parámetro del constructor) de los números de Fibonacci.

Permítenos recordarte: los números de Fibonacci(Fibi) se definen de la siguiente


manera:

Fib1  = 1
Fib2  = 1
Fibi  = Fibi-1  + Fibi-2

En otras palabras:

 Los primeros dos números de la serie Fibonacci son 1.


 Cualquier otro número de Fibonacci es la suma de los dos anteriores (por
ejemplo, Fib3 = 2, Fib4 = 3, Fib5 = 5, y así sucesivamente).

Vamos a ver el código:

class Fib:

def __init__(self, nn):


print("__init__")

self.__n = nn

self.__i = 0

self.__p1 = self.__p2 = 1

def __iter__(self):

print("__iter__")

return self

def __next__(self):

print("__next__")

self.__i += 1

if self.__i > self.__n:

raise StopIteration

if self.__i in [1, 2]:

return 1

ret = self.__p1 + self.__p2

self.__p1, self.__p2 = self.__p2, ret

return ret

for i in Fib(10):

print(i)

 Líneas 2 a 6: el constructor de la clase imprime un mensaje (lo usaremos para


rastrear el comportamiento de la clase), se preparan algunas variables:
( __n  para almacenar el límite de la serie,  __i  para rastrear el número actual de
la serie Fibonacci, y  __p1  junto con  __p2  para guardar los dos números
anteriores).

 Líneas 8 a 10: el método  __iter__  está obligado a devolver el objeto iterador


en sí mismo; su propósito puede ser un poco ambiguo aquí, pero no hay
misterio; trata de imaginar un objeto que no sea un iterador (por ejemplo, es
una colección de algunas entidades), pero uno de sus componentes es un
iterador capaz de escanear la colección; el método  __iter__  debe extraer el
iterador y confiarle la ejecución del protocolo de iteración; como puedes
ver, el método comienza su acción imprimiendo un mensaje.

 Líneas 12 a 21: el método  __next__  es responsable de crear la secuencia; es


algo largo, pero esto debería hacerlo más legible; primero, imprime un
mensaje, luego actualiza el número de valores deseados y, si llega al final de la
secuencia, el método interrumpe la iteración al generar la excepción
StopIteration; el resto del código es simple y refleja con precisión la definición
que te mostramos anteriormente.

 Las líneas 23 y 24 hacen uso del iterador.

El código produce el siguiente resultado:

__init__

__iter__

__next__

__next__

__next__

__next__

__next__

__next__

__next__

13

__next__

21

__next__

34

__next__

55
__next__

Observa:

 El objeto iterador se instancia primero.


 Después, Python invoca el método  __iter__  para acceder al iterador real.
 El método  __next__  se invoca once veces: las primeras diez veces produce
valores útiles, mientras que la ultima finaliza la iteración.

Generadores, dónde encontrarlos: continuación


El ejemplo muestra una solución donde el objeto iterador es parte de una clase
más compleja.

El código no es sofisticado, pero presenta el concepto de una manera clara.

Echa un vistazo al código en el editor.

class Fib:

def __init__(self, nn):

self.__n = nn

self.__i = 0

self.__p1 = self.__p2 = 1

def __iter__(self):

print("Fib iter")

return self

def __next__(self):

self.__i += 1

if self.__i > self.__n:

raise StopIteration

if self.__i in [1, 2]:

return 1
ret = self.__p1 + self.__p2

self.__p1, self.__p2 = self.__p2, ret

return ret

class Class:

def __init__(self, n):

self.__iter = Fib(n)

def __iter__(self):

print("Class iter")

return self.__iter;

object = Class(8)

for i in object:

print(i)

Hemos puesto el iterador  Fib  dentro de otra clase (podemos decir que lo hemos
compuesto dentro de la clase  Class ). Se instancia junto con el objeto de  Class .

El objeto de la clase se puede usar como un iterador cuando (y solo cuando) responde
positivamente a la invocación  __iter__  - esta clase puede hacerlo, y si se invoca de
esta manera, proporciona un objeto capaz de obedecer el protocolo de iteración.

Es por eso que la salida del código es la misma que anteriormente, aunque el objeto
de la clase  Fib  no se usa explícitamente dentro del contexto del bucle  for .

Resultado:

Class iter
1
1
2
3
5
8
13
21

La sentencia yield
El protocolo iterador no es difícil de entender y usar, pero también es indiscutible que el protocolo
es bastante inconveniente.

La principal molestia que tiene es que necesita guardar el estado de la iteración en las


invocaciones subsequentes de  __iter__ .

Por ejemplo, el iterador  Fib  se ve obligado a almacenar con precisión el lugar en el que se detuvo la
última invocación (es decir, el número evaluado y los valores de los dos elementos anteriores). Esto
hace que el código sea más grande y menos comprensible.

Es por eso que Python ofrece una forma mucho más efectiva, conveniente y elegante de escribir
iteradores.

El concepto se basa fundamentalmente en un mecanismo muy específico proporcionado por la


palabra clave reservada  yield .

Se puede ver a la palabra clave reservada  yield  como un hermano más inteligente de la
sentencia  return , con una diferencia esencial.

Echa un vistazo a esta función:

def fun(n):

for i in range(n):

return i

Se ve extraño, ¿no? Está claro que el bucle  for  no tiene posibilidad de terminar su primera
ejecución, ya que el  return  lo romperá irrevocablemente.

Además, invocar la función no cambiará nada: el bucle  for  comenzará desde cero y se romperá
inmediatamente.

Podemos decir que dicha función no puede guardar y restaurar su estado en invocaciones
posteriores.

Esto también significa que una función como esta no se puede usar como generador.

Hemos reemplazado exactamente una palabra en el código, ¿puedes verla?

def fun(n):

for i in range(n):

yield i
Hemos puesto  yield  en lugar de  return . Esta pequeña enmienda convierte la función en un
generador, y el ejecutar la sentencia  yield  tiene algunos efectos muy interesantes.

Primeramente, proporciona el valor de la expresión especificada después de la palabra clave


reservada  yield , al igual que  return , pero no pierde el estado de la función.

Todos los valores de las variables están congelados y esperan la próxima invocación, cuando se
reanuda la ejecución (no desde cero, como ocurre después de un  return ).

Hay una limitación importante: dicha función no debe invocarse explícitamente ya que no es una
función; es un objeto generador.

La invocación devolverá el identificador del objeto, no la serie que esperamos del generador.

Debido a las mismas razones, la función anterior (la que tiene el  return ) solo se puede invocar
explícitamente y no se debe usar como generador.

Cómo construir un generador:


Permítenos mostrarte el nuevo generador en acción.

Así es como podemos usarlo:

def fun(n):

for i in range(n):

yield i

for v in fun(5):

print(v)

¿Puedes adivinar la salida?

Revisar

Cómo construir tu propio generador


¿Qué pasa si necesitas un generador para producir las primeras n potencias de  2 ?

Nada difícil. Solo mira el código en el editor.

def potenciasDe2(n):

potencia = 1

for i in range(n):

yield potencia

potencia *= 2

for v in potenciasDe2(8):

print(v)

¿Puedes adivinar la salida? Ejecuta el código para verificar tus conjeturas.

1
2
4
8
16
32
64
128

Los generadores también pueden usarse dentro de listas de comprensión, como


aqui:

def potenciasDe2(n):

potencia = 1

for i in range(n):

yield potencia

potencia *= 2

t = [x for x in potenciasDe2(5)]

print(t)

Ejecuta el ejemplo y verifica la salida.


La función  list()  puede transformar una serie de invocaciones de generador
subsequentes en una lista real:

def potenciasDe2(n):

potencia = 1

for i in range(n):

yield potencia

potencia *= 2

t = list(potenciasDe2(3))

print(t)

Nuevamente, intenta predecir el resultado y ejecuta el código para verificar tus


predicciones.

Además, el contexto creado por el operador  in  también te permite usar un


generador.

El ejemplo muestra cómo hacerlo:

def potenciasDe2(n):

potencia= 1

for i in range(n):

yield potencia

potencia*= 2

for i in range(20):

if i in potenciasDe2(4):

print(i)

¿Cuál es la salida del código? Ejecuta el programa y verifica.


Ahora veamos un Generador de números de la serie Fibonacci implementando lo
anterior.

Aquí está:

def Fib(n):

p = pp = 1

for i in range(n):

if i in [0, 1]:

yield 1

else:

n = p + pp

pp, p = p, n

yield n

fibs = list(Fib(10))

print(fibs)

Adivina la salida (una lista) producida por el generador y ejecuta el código para
verificar si tenías razón.

Más sobre comprensión de listas


Debes poder recordar las reglas que rigen la creación y el uso de un fenómeno de
Python llamado comprensión de listas: una forma simple de crear listas y sus
contenidos.

En caso de que lo necesites, te proporcionamos un recordatorio en el editor.

Existen dos partes dentro del código, ambas crean una lista que contiene algunas de
las primeras potencias naturales de diez.

La primer parte utiliza una forma rutinaria del bucle  for , mientras que la segunda
hace uso de la comprensión de listas y construye la lista en el momento, sin necesidad
de un bucle o cualquier otro código.

Pareciera que la lista se crea dentro de sí misma; esto es falso, ya que Python tiene
que realizar casi las mismas operaciones que en la primera parte, pero el segundo
formalismo es simplemente más elegante y le evita al lector cualquier detalle
innecesario.

El ejemplo genera dos líneas idénticas que contienen el siguiente texto:

listaUno = []

for ex in range(6):

listaUno.append(10 ** ex)

listaDos = [10 ** ex for ex in range(6)]

print(listaUno)

print(listaDos)

[1, 10, 100, 1000, 10000, 100000]

Más sobre comprensión de listas: continuación


Hay una sintaxis muy interesante que queremos mostrarte ahora. Su usabilidad no se
limita a la comprensión de listas.

Es una expresión condicional: una forma de seleccionar uno de dos valores


diferentes en función del resultado de una expresión booleana.

Observa :

expresión_uno if condición else expresión_dos

Puede parecer un poco sorprendente a primera vista, pero hay que tener en cuenta
que no es una instrucción condicional. Además, no es una instrucción en lo
absoluto. Es un operador.

El valor que proporciona es expresión_uno cuando la condición es  True  (verdadero),


y expresión_dos cuando sea falso.

Un buen ejemplo te dirá más. Mira el código en el editor.

lst = []

for x in range(10):

lst.append(1 if x % 2 == 0 else 0)

print(lst)
Resultado:

[1, 0, 1, 0, 1, 0, 1, 0, 1, 0]

El código llena una lista con  unos  y  ceros , si el índice de un elemento particular es
impar, el elemento se establece en  0 , y a  1  de lo contrario.

¿Simple? Quizás no a primera vista. ¿Elegante? Indiscutiblemente.

¿Se puede usar el mismo truco dentro de una lista de comprensión? Sí, por supuesto.

Más sobre comprensión de listas: continuación


Mira el ejemplo en el editor.

lst = [1 if x % 2 == 0 else 0 for x in range(10)]

print(lst)

Compacidad y elegancia: estas dos palabras vienen a la mente al mirar el código.

Entonces, ¿qué tienen en común, generadores y listas de comprensión? ¿Hay alguna


conexión entre ellos? Sí. Una conexión algo suelta, pero inequívoca.

Solo un cambio puede convertir cualquier comprensión en un generador.

Ahora mira el código a continuación y ve si puedes encontrar el detalle que convierte


una comprensión de la lista en un generador:

lst = [1 if x % 2 == 0 else 0 for x in range(10)]

genr = (1 if x % 2 == 0 else 0 for x in range(10))

for v in lst:

print(v, end=" ")

print()

for v in genr:

print(v, end=" ")

print()
Son los paréntesis. Los corchetes hacen una comprensión, los paréntesis hacen un
generador.

El código, cuando se ejecuta, produce dos líneas idénticas:

1 0 1 0 1 0 1 0 1 0

1 0 1 0 1 0 1 0 1 0

¿Cómo puedes saber que la segunda asignación crea un generador, no una lista?

Hay algunas pruebas que podemos mostrarte. Aplica la función  len()  a ambas
entidades.

len(lst)  dará como resultado  10 , claro y predecible,  len(genr)  provocará una


excepción y verás el siguiente mensaje:

TypeError: object of type 'generator' has no len()

Por supuesto, guardar la lista o el generador no es necesario; puedes crearlos


exactamente en el lugar donde los necesites, como aquí:

for v in [1 if x % 2 == 0 else 0 for x in range(10)]:

print(v, end=" ")

print()

for v in (1 if x % 2 == 0 else 0 for x in range(10)):

print(v, end=" ")

print()

Nota: la misma apariencia de la salida no significa que ambos bucles funcionen de la


misma manera. En el primer bucle, la lista se crea (y se itera) como un todo; en
realidad, existe cuando se ejecuta el bucle.

En el segundo bucle, no hay ninguna lista, solo hay valores subsequentes producidos
por el generador, uno por uno.

Realiza tus propios experimentos.

La función lambda
La función  lambda  es un concepto tomado de las matemáticas, más específicamente, de una parte
llamada el cálculo Lambda, pero estos dos fenómenos no son iguales.

Los matemáticos usan el cálculo Lambda en sistemas formales conectados con: la lógica, la
recursividad o la demostrabilidad de teoremas. Los programadores usan la función  lambda  para
simplificar el código, hacerlo más claro y fácil de entender.

Una función  lambda  es una función sin nombre (también puedes llamarla una función anónima).
Por supuesto, tal afirmación plantea inmediatamente la pregunta: ¿cómo se usa algo que no se puede
identificar?

Afortunadamente, no es un problema, ya que se puede mandar llamar dicha función si realmente se


necesita, pero, en muchos casos la función  lambda  puede existir y funcionar mientras permanece
completamente de incógnito.

La declaración de la función  lambda  no se parece a una declaración de función normal;


compruébalo tu mismo:

lambda parámetros: expresión

Tal cláusula devuelve el valor de la expresión al tomar en cuenta el valor del


argumento  lambda  actual.

Como de costumbre, un ejemplo será útil. Nuestro ejemplo usa tres funciones  lambda , pero con
nombres. Analizalo cuidadosamente:

dos = lambda : 2
cuadrado = lambda x : x * x
potencia = lambda x, y : x ** y

for a in range(-2, 3):


print(cuadrado(a), end=" ")
print(potencia(a, dos()))

Vamos a analizarlo:

 La primer  lambda  es una función anónima sin parametros que siempre devuelve un  2 .
Como se ha asignado a una variable llamada  dos , podemos decir que la función ya no es
anónima, y se puede usar su nombre para invocarla.

 La segunda es una función anónima de un parámetro que devuelve el valor de su


argumento al cuadrado. Se ha nombrado también como tal.

 La tercer  lambda  toma dos parametros y devuelve el valor del primero elevado al
segundo. El nombre de la variable que lleva la  lambda  habla por si mismo.
El programa produce el siguiente resultado:

4 4
1 1
0 0
1 1
4 4

Este ejemplo es lo suficientemente claro como para mostrar cómo se declaran las
funciones  lambda  y cómo se comportan, pero no dice nada acerca de por qué son necesarias y para
qué se usan, ya que se pueden reemplazar con funciones de Python de rutina.

¿Dónde está el beneficio?

¿Cómo usar lambdas y para qué?


La parte más interesante de usar lambdas aparece cuando puedes usarlas en su
forma pura - como partes anónimas de código destinadas a evaluar un resultado.

Imagina que necesitamos una función (la nombraremos  imprimirfuncion ) que


imprime los valores de una (otra) función dada para un conjunto de argumentos
seleccionados.

Queremos que  imprimirfuncion  sea universal - debería aceptar un conjunto de


argumentos incluidos en una lista y una función a ser evaluada, ambos como
argumentos; no queremos codificar nada.

Mira el ejemplo en el editor. Así es como hemos implementado la idea.

def imprimirfuncion(args, fun):

for x in args:

print('f(', x,')=', fun(x), sep='')

def poli(x):

return 2 * x**2 - 4 * x + 2

imprimirfuncion([x for x in range(-2, 3)], poli)

Analicémoslo. La función  imprimirfuncion()  toma dos parámetros:

 El primero, una lista de argumentos para los que queremos imprimir los
resultados.
 El segundo, una función que debe invocarse tantas veces como el número de
valores que se recopilan dentro del primer parámetro.
Nota: También hemos definido una función llamada  poli()  - esta es la función cuyos
valores vamos a imprimir. El cálculo que realiza la función no es muy sofisticado: es el
polinomio (de ahí su nombre) de la forma:

f(x) = 2x2 - 4x + 2

El nombre de la función se pasa a  imprimirfuncion()  junto con un conjunto de cinco


argumentos diferentes: el conjunto está construido con una cláusula de comprensión
de la lista.

El código imprime las siguientes líneas:

f(-2)=18
f(-1)=8
f(0)=2
f(1)=0
f(2)=2

¿Podemos evitar definir la función  poli() , ya que no la vamos a usar más de una vez?
Sí, podemos: este es el beneficio que puede aportar una función lambda.

Mira el ejemplo de abajo. ¿Puedes ver la diferencia?

def imprimirfuncion(args, fun):


for x in args:
print('f(', x,')=', fun(x), sep='')

imprimirfuncion([x for x in range(-2, 3)], lambda x: 2 * x**2 - 4 *


x + 2)

La función  imprimirfuncion() se ha mantenido exactamente igual, pero no hay una


función  poli() . Ya no la necesitamos, ya que el polinomio ahora está directamente
dentro de la invocación de la función  imprimirfuncion()  en forma de una lambda
definida de la siguiente manera:  lambda x: 2 * x**2 - 4 * x + 2 .

El código se ha vuelto más corto, más claro y más legible.

Permítenos mostrarte otro lugar donde las lambdas pueden ser útiles. Comenzaremos
con una descripción de  map() , una función de Python incorporada. Su nombre no es
demasiado descriptivo, su idea es simple y la función en sí es muy utilizable.

Lambdas y la función map()
En el más simple de todos los casos posibles, la función  map()  toma dos argumentos:
 Una función.
 Una lista.
map(función, lista)

La descripción anterior está extremadamente simplificada, ya que:

 El segundo argumento  map()  puede ser cualquier entidad que se pueda iterar
(por ejemplo, una tupla o un generador).
 map()  puede aceptar más de dos argumentos.

La función  map()  aplica la función pasada por su primer argumento a todos los
elementos de su segundo argumento y devuelve un iterador que entrega todos
los resultados de funciones posteriores. Puedes usar el iterador resultante en un
bucle o convertirlo en una lista usando la función  list() .

¿Puedes ver un papel para una lambda aquí?

Observa el código en el editor: hemos usado dos lambdas en él.

lista1 = [x for x in range(5)]

lista2 = list(map(lambda x: 2 ** x, lista1))

print(lista2)

for x in map(lambda x: x * x, lista2):

print(x, end=' ')

print()

Resultado:

[1, 2, 4, 8, 16]
1 4 16 64 256

Esta es la explicación:

 Se construye la  lista1  con valores del  0  al  4 .


 Después, se utiliza  map  junto con la primer  lambda  para crear una nueva lista
en la que todos los elementos han sido evaluados como  2  elevado a la
potencia tomada del elemento correspondiente de  lista1 .
 lista2  es entonces impresa.
 En el siguiente paso, se usa nuevamente la función  map()  para hacer uso del
generador que devuelve, e imprimir directamente todos los valores que
entrega; como puedes ver, hemos usado el segundo  lambda  aquí - solo eleva al
cuadrado cada elemento de  lista2 .
Intenta imaginar el mismo código sin lambdas. ¿Sería mejor? Es improbable.

Lambdas y la función filter()
Otra función de Python que se puede embellecer significativamente mediante la
aplicación de una lambda es  filter() .

Espera el mismo tipo de argumentos que  map() , pero hace algo diferente - filtra su
segundo argumento mientras es guiado por direcciones que fluyen desde la
función especificada en el primer argumento (la función se invoca para cada
elemento de la lista, al igual que en  map()  ).

Los elementos que regresan  True  de la función pasan el filtro - los otros son
rechazados.

El ejemplo en el editor muestra la función  filter()  en acción.

from random import seed, randint

seed()

data = [ randint(-10,10) for x in range(5) ]

filtered = list(filter(lambda x: x > 0 and x % 2 == 0, data))

print(data)

print(filtered)

Nota: hemos hecho uso del módulo  random  para inicializar el generador de números
aleatorios (que no debe confundirse con los generadores de los que acabamos de
hablar) con la función  seed() , para producir cinco valores enteros aleatorios de  -
10  a  10  usando la función  randint() .

Luego se filtra la lista y solo se aceptan los números que son pares y mayores que
cero.

Por supuesto, no es probable que recibas los mismos resultados, pero así es como se
veían nuestros resultados:

[6, 3, 3, 2, -7]

[6, 2]

Una breve explicación de cierres


Comencemos con una definición: cierres es una técnica que permite almacenar
valores a pesar de que el contexto en el que se crearon ya no existe..
¿Complicado? Un poco.

Analicemos un ejemplo simple:

def exterior(par):
loc = par

var = 1
exterior(var)

print(var)
print(loc)

El ejemplo es obviamente erróneo.

Las dos últimas líneas provocarán una excepción NameError - ni  par  ni  loc  son
accesibles fuera de la función. Ambas variables existen cuando y solo cuando la
función  exterior()  esta siendo ejecutada.

Mira el ejemplo en el editor. Hemos modificado el código significativamente.

def exterior(par):

loc = par

def interior():

return loc

return interior

var = 1

fun = exterior(var)

print(fun())

Hay un elemento completamente nuevo - una función (llamada  interior ) dentro de


otra función (llamada  exterior ).

¿Como funciona? Como cualquier otra función excepto por el hecho de


que  interior()  solo se puede invocar desde dentro de  exterior() . Podemos decir
que  interior()  es una herramienta privada de  exterior() , ninguna otra parte del
código la puede acceder.
Observa cuidadosamente:

 La función  interior()  devuelve el valor de la variable accesible dentro de su


alcance, ya que  interior()  puede utilizar cualquiera de las entidades a
disposición de  exterior() .
 La función  exterior()  devuelve la función  interior()  por si misma; mejor
dicho, devuelve una copia de la función  interior()  al momento de la
invocación de la función  exterior() ; la función congelada contiene su
entorno completo, incluido el estado de todas las variables locales, lo que
también significa que el valor de  loc  se retiene con éxito,
aunque  exterior()  ya ha dejado de existir.

En efecto, el código es totalmente válido y genera:

La función devuelta durante la invocación de  exterior()  es un cierre.

Una breve explicación de cierres: continuación


Un cierre se debe invocar exactamente de la misma manera en que se ha
declarado.

En el ejemplo anterior (vea el código a continuación):

def exterior(par):
loc = par
def interior():
return loc
return interior

var = 1
fun = exterior(var)
print(fun()))

La función  interior()  no tenía parámetros, por lo que tuvimos que invocarla sin
argumentos.

Ahora mira el código en el editor. Es totalmente posible declarar un cierre equipado


con un número arbitrario de parámetros, por ejemplo, al igual que la
función  potencia() .

def crearcierre(par):

loc = par

def potencia(p):
return p ** loc

return potencia

fsqr = crearcierre(2)

fcub = crearcierre(3)

for i in range(5):

print(i, fsqr(i), fcub(i))

Esto significa que el cierre no solo utiliza el ambiente congelado, sino que también
puede modificar su comportamiento utilizando valores tomados del exterior.

Este ejemplo muestra una circunstancia más interesante: puedes crear tantos cierres
como quieras usando el mismo código. Esto se hace con una función
llamada  crearcierre() . Nota:

 El primer cierre obtenido de  crearcierre()  define una herramienta que eleva
al cuadrado su argumento.
 El segundo está diseñado para elevar el argumento al cubo.

Es por eso que el código produce el siguiente resultado:

0 0 0
1 1 1
2 4 8
3 9 27
4 16 64

 6.1.8.1 Procesando archivos

Accediendo a archivos desde el código en Python


Uno de los problemas más comunes en el trabajo del desarrollador es procesar datos almacenados
en archivos que generalmente se almacenan físicamente utilizando dispositivos de almacenamiento:
discos duros, ópticos, de red o de estado sólido.

Es fácil imaginar un programa que clasifique 20 números, y es igualmente fácil imaginar que el
usuario de este programa ingrese estos veinte números directamente desde el teclado.

Es mucho más difícil imaginar la misma tarea cuando hay 20,000 números para ordenar, y no existe
un solo usuario que pueda ingresar estos números sin cometer un error.

Es mucho más fácil imaginar que estos números se almacenan en el archivo que lee el programa. El
programa clasifica los números y no los envía a la pantalla, sino que crea un nuevo archivo y guarda
la secuencia ordenada de números allí.
Si queremos implementar una base de datos simple, la única forma de almacenar la información
entre ejecuciones del programa es guardarla en un archivo (o archivos si tu base de datos es más
compleja).

Es un principio que cualquier problema de programación no simple se basa en el uso de archivos, ya


sea que procese imágenes (almacenadas en archivos), multiplique matrices (almacenadas en
archivos) o calcule salarios e impuestos (lectura de datos almacenados en archivos).

Puedes preguntarte por qué hemos esperado hasta ahora para mostrarte esto.

La respuesta es muy simple: la forma en que Python accede y procesa los archivos se implementa
utilizando un conjunto consistente de objetos. No hay mejor momento para hablar de esto.

Nombres de archivos
Los diferentes sistemas operativos pueden tratar a los archivos de diferentes maneras. Por ejemplo,
Windows usa una convención de nomenclatura diferente a la adoptada en los sistemas Unix/Linux.

Si utilizamos la noción de un nombre de archivo canónico (un nombre que define de forma exclusiva
la ubicación del archivo, independientemente de su nivel en el árbol de directorios), podemos darnos
cuenta de que estos nombres se ven diferentes en Windows y en Unix/Linux:
Como puedes ver, los sistemas derivados de Unix/Linux no usan la letra de la unidad de disco (p.
Ejemplo,  C: ) y todos los directorios crecen desde un directorio raíz llamado  / , mientras que los
sistemas Windows reconocen el directorio raíz como  \ .

Además, los nombres de archivo de sistemas Unix/Linux distinguen entre mayúsculas y minúsculas.
Los sistemas Windows almacenan mayúsculas y minúsculas en el nombre del archivo, pero no
distinguen entre ellas.

Esto significa que estas dos cadenas:

EsteEsElNombreDelArchivo
y
esteeselnombredelarchivo

describen dos archivos diferentes en sistemas Unix/Linux, pero tienen el mismo nombre para un solo
archivo en sistemas Windows.

La diferencia principal y más llamativa es que debes usar dos separadores diferentes para los
nombres de directorio:  \  en Windows y  /  en Unix/Linux.

Esta diferencia no es muy importante para el usuario normal, pero es muy importante al escribir
programas en Python.

Para entender por qué, intenta recordar el papel muy específico que desempeña  \  dentro de las
cadenas en Python.
Nombres de Archivo: continuación
Supongamos que estás interesado en un archivo en particular ubicado en el directorio dir, y con el
nombre de archivo.

Supongamos también que deseas asignar a una cadena el nombre del archivo.

En sistemas Unix/Linux, se ve de la siguiente manera:

nombre = "/dir/archivo"

Pero si intentas codificarlo para el sistema Windows:

nombre = "\dir\archivo"

obtendrás una sorpresa desagradable: Python generará un error o la ejecución del programa se
comportará de manera extraña, como si el nombre del archivo se hubiera distorsionado de alguna
manera.

De hecho, no es extraño en lo absoluto, pero es bastante obvio y natural. Python usa la  \  como un
caracter de escape (como  \n ).

Esto significa que los nombres de archivo de Windows deben escribirse de la siguiente manera:

nombre = "\\dir\\archivo"

Afortunadamente, también hay una solución más. Python es lo suficientemente inteligente como
para poder convertir diagonales en diagonales invertidas cada vez que descubre que el sistema
operativo lo requiere.

Esto significa que cualquiera de las siguientes asignaciones:

nombre = "/dir/archivo"

nombre = "c:/dir/archivo"

funcionará también con Windows.

Cualquier programa escrito en Python (y no solo en Python, porque esa convención se aplica a
prácticamente todos los lenguajes de programación) no se comunica con los archivos directamente,
sino a través de algunas entidades abstractas que se nombran de manera diferente en los distintos
lenguajes o entornos, los términos más utilizados son handles (un tipo de puntero
inteligente) o streams (una especie de canal) (los usaremos como sinónimos aquí).

El programador, que tiene un conjunto de funciones y métodos, puede realizar ciertas operaciones en
el stream, que afectan los archivos reales utilizando mecanismos contenidos en el núcleo del sistema
operativo.
De esta forma, puedes implementar el proceso de acceso a cualquier archivo, incluso cuando el
nombre del archivo es desconocido al momento de escribir el programa.

Las operaciones realizadas con el stream abstracto reflejan las actividades relacionadas con el
archivo físico.

Para conectar (vincular) el stream con el archivo, es necesario realizar una operación explícita.

La operación de conectar un stream con un archivo es llamada abrir el archivo, mientras que


desconectar el enlace se denomina cerrar el archivo.

Por lo tanto, la conclusión es que la primera operación realizada en el stream es siempre  open
(abrir)  y la ultima es  close (cerrar) . El programa, en efecto, es libre de manipular el
stream entre estos dos eventos y manejar el archivo asociado.

Esta libertad está limitada por las características físicas del archivo y la forma en que se abrió el
archivo.

Digamos nuevamente que la apertura del stream puede fallar, y puede ocurrir debido a varias
razones: la más común es la falta de un archivo con un nombre específico.

También puede suceder que el archivo físico exista, pero el programa no puede abrirlo. También
existe el riesgo de que el programa haya abierto demasiados streams, y el sistema operativo
específico puede no permitir la apertura simultánea de más de n archivos (por ejemplo, 200).

Un programa bien escrito debe detectar estas aperturas fallidas y reaccionar en consecuencia.
Streams para Archivos
La apertura del stream no solo está asociada con el archivo, sino que también se debe declarar la
manera en que se procesará el stream. Esta declaración se llama un open mode (modo abierto).

Si la apertura es exitosa, el programa solo podrá realizar las operaciones que sean consistentes
con el modo abierto declarado.

Hay dos operaciones básicas a realizar con el stream:

 Lectura del stream: las porciones de los datos se recuperan del archivo y se colocan en un
área de memoria administrada por el programa (por ejemplo, una variable).
 Escritura del stream: Las porciones de los datos de la memoria (por ejemplo, una variable)
se transfieren al archivo.

Hay tres modos básicos utilizados para abrir un stream:

 Modo Lectura: un stream abierto en este modo permite solo operaciones de lectura;


intentar escribir en la transmisión provocará una excepción (la excepción se
llama UnsupportedOperation, la cual hereda el OSError y el ValueError, y
proviene del módulo io).
 Modo Escritura: un stream abierto en este modo permite solo operaciones de escritura;
intentar leer el stream provocará la excepción mencionada anteriormente.
 Modo Actualizar: un stream abierto en este modo permite tanto lectura como escritura.

Antes de discutir cómo manipular los streams, te debemos una explicación. El stream se comporta
casi como una grabadora.

Cuando lees algo de un stream, un cabezal virtual se mueve sobre la transmisión de acuerdo con el
número de bytes transferidos desde el stream.

Cuando escribes algo en el stream el mismo cabezal se mueve a lo largo del stream registrando los
datos de la memoria.

Siempre que hablemos de leer y escribir en el stream, trata de imaginar esta analogía. Los libros de
programación se refieren a este mecanismo como la posición actual del archivo, aquí también
usaremos este término.
Ahora es necesario mostrarte el objeto responsable de representar los streams en los programas.

Manejo de Archivos
Python supone que cada archivo está oculto detrás de un objeto de una clase adecuada.

Por supuesto, es difícil no preguntar cómo interpretar la palabra adecuada.

Los archivos se pueden procesar de muchas maneras diferentes: algunos dependen del contenido del
archivo, otros de las intenciones del programador.

En cualquier caso, diferentes archivos pueden requerir diferentes conjuntos de operaciones y


comportarse de diferentes maneras.

Un objeto de una clase adecuada es creado cuando abres el archivo y lo aniquilas al momento de
cerrarlo.

Entre estos dos eventos, puedes usar el objeto para especificar qué operaciones se deben realizar en
un stream en particular. Las operaciones que puedes usar están impuestas por la forma en que
abriste el archivo.

En general, el objeto proviene de una de las clases que se muestran aquí:


Nota: nunca se utiliza el constructor para dar vida a estos objetos. La unica forma de obtenerlos es
invocar la función llamada  open() .

La función analiza los argumentos proporcionados y crea automáticamente el objeto requerido.

Si deseas deshacerte del objeto, invoca el método denominado  close() .

La invocación cortará la conexión con el objeto y el archivo, y eliminará el objeto.

Para nuestros propósitos, solo nos ocuparemos de los streams representados por los
objetos  BufferIOBase  y  TextIOBase . Entenderás por qué pronto.

Manejo de Archivos: continuación


Debido al tipo de contenido de los streams, todos se dividen en tipo texto y binario.

Los streams de texto están estructurados en líneas; es decir, contienen caracteres tipográficos (letras,
dígitos, signos de puntuación, etc.) dispuestos en filas (líneas), como se ve a simple vista cuando se
mira el contenido del archivo en el editor.

Este tipo de archivo es escrito (o leído) principalmente carácter por carácter, o línea por línea.

Los streams binarios no contienen texto, sino una secuencia de bytes de cualquier valor. Esta
secuencia puede ser, por ejemplo, un programa ejecutable, una imagen, un audio o un videoclip, un
archivo de base de datos, etc.

Debido a que estos archivos no contienen líneas, las lecturas y escrituras se relacionan con porciones
de datos de cualquier tamaño. Por lo tanto, los datos se leen y escriben byte a byte, o bloque a
bloque, donde el tamaño del bloque generalmente varía de uno a un valor elegido arbitrariamente.

Ahora viene un problema pequeño. En los sistemas Unix/Linux, los extremos de la línea están
marcados por un solo carácter llamado  LF  (código ASCII 10) designado en los programas de Python
como  \n .

Otros sistemas operativos, especialmente los derivados del sistema prehistórico CP/M (que también
aplica a los sistemas de la familia Windows) utilizan una convención diferente: el final de la línea
está marcada por un par de caracteres,  CR  y  LF  (códigos ASCII 13 y 10) los cuales se puede
codificar como  \r\n .
Esta ambigüedad puede causar varias consecuencias desagradables.

Si creas un programa responsable de procesar un archivo de texto y está escrito para Windows,
puedes reconocer los extremos de las líneas al encontrar los caracteres  \r\n , pero si el mismo
programa se ejecuta en un entorno Unix/Linux será completamente inútil, y viceversa: el programa
escrito para sistemas Unix/Linux podría ser inútil en Windows.

Estas características indeseables del programa, que impiden o dificultan el uso del programa en
diferentes entornos, se denomina falta de portabilidad.

Del mismo modo, el rasgo del programa que permite la ejecución en diferentes entornos se
llama portabilidad. Un programa dotado de tal rasgo se llama programa portable.

Manejo de archivos: continuación


Dado que los problemas de portabilidad eran (y siguen siendo) muy graves, se tomó la decisión de
resolver definitivamente el problema de una manera que no atraiga mucho la atención del
desarrollador.

Se realizó a nivel de clases, que son responsables de leer y escribir caracteres hacia y desde el
stream. Funciona de la siguiente manera:

 Cuando el stream está abierto y se recomienda que los datos en el archivo asociado se
procesen como texto (o no existe tal aviso), se cambia al modo texto.

 Durante la lectura y escritura de líneas desde y hacia el archivo asociado, no ocurre nada
especial en el entorno Unix, pero cuando se realizan las mismas operaciones en el entorno
Windows, un proceso llamado traducción de caracteres de nueva línea ocurre: cuando
lees una línea del archivo, cada par de caracteres  \r\n  se reemplaza con un solo caracter  \
n , y viceversa; durante las operaciones de escritura, cada caracter  \n  se reemplaza con un
par de caracteres  \r\n .
 El mecanismo es completamente transparente para el programa, el cual puede escribirse
como si estuviera destinado a procesar archivos de texto Unix/Linux solamente; el código
fuente ejecutado en un entorno Windows también funcionará correctamente.

 Cuando el stream está abierto, su contenido se toma tal cual es, sin ninguna conversión -
no se agregan, ni se omiten bytes.

Abriendo los streams


El abrir un stream se realiza mediante una función que se puede invocar de la siguiente manera:

stream = open(file, mode = 'r', encoding = None)

Vamos a analizarlo:

 El nombre de la función ( open ) habla por si mismo; si la apertura es exitosa, la función


devuelve un objeto stream; de lo contrario, se genera una excepción (por
ejemplo, FileNotFoundError si el archivo que vas a leer no existe).
 El primer parámetro de la función ( file ) especifica el nombre del archivo que se asociará
al stream.
 El segundo parámetro ( mode ) especifica el modo de apertura utilizado para el stream; es
una cadena llena de una secuencia de caracteres, y cada uno de ellos tiene su propio
significado especial (más detalles pronto).
 El tercer parámetro ( encoding ) especifica el tipo de codificación (por ejemplo, UTF-8
cuando se trabaja con archivos de texto).
 La apertura debe ser la primera operación realizada en el stream.

Nota: el modo y los argumentos de codificación pueden omitirse; en dado caso, se tomarán sus
valores predeterminados. El modo de apertura predeterminado es leer en modo de texto, mientras
que la codificación predeterminada depende de la plataforma utilizada.

Permítenos ahora presentarte los modos de apertura más importantes y útiles. ¿Listo?
Abriendo los streams: modos
Modo de apertura  r : lectura

 El stream será abierto en modo lectura.


 El archivo asociado con el stream debe existir y tiene que ser legible, de lo contrario la
función  open()  lanzará una excepción.

Modo de apertura  w : escritura

 El stream será abierto en modo escritura.


 El archivo asociado con el stream no necesita existir. Si no existe, se creará; si existe, se
truncará a la longitud de cero (se borrá); si la creación no es posible (por ejemplo, debido a
los permisos del sistema) la función  open()  lanzará una excepción.

Modo de apertura  a : adjuntar

 El stream será abierto en modo adjuntar.


 El archivo asociado con el stream no necesita existir; si no existe, se creará; si existe, el
cabezal de grabación virtual se establecerá al final del archivo (el contenido anterior del
archivo permanece intacto).

Modo de apertura  r+ : leer y actualizar

 El stream será abierto en modo leer y actualizar.


 El archivo asociado con el stream debe existir y tiene que ser escribible, de lo contrario la
función  open()  lanzará una excepción.
 Se permiten operaciones de lectura y escritura en el stream.

Modo de apertura  w+ : escribir y actualizar

 El stream será abierto en modo escribir y actualizar.


 El archivo asociado con el stream no necesita existir; si no existe, se creará; el contenido
anterior del archivo permanece intacto.
 Se permiten operaciones de lectura y escritura en el stream.

Seleccionando modo de texto y modo binario


Si hay una letra  b  al final de la cadena del modo significa que el stream se debe abrir en el modo
binario.

Si la cadena del modo termina con una letra  t  el stream es abierto en modo texto.

El modo texto es el comportamiento predeterminado que se utiliza cuando no se especifica ya sea


modo binario o texto.

Finalmente, la apertura exitosa del archivo establecerá la posición actual del archivo (el cabezal
virtual de lectura/escritura) antes del primer byte del archivo si el modo no es  a  y después del
último byte del archivo si el modo es  a .
Modo texto Modo binario Descripción

rt rb lectura

wt wb escritura

at ab adjuntar

r+t r+b leer y actualizar

w+t w+b escribir y actualizar

EXTRA

También puedes abrir un archivo para su creación exclusiva. Puedes hacer esto usando el modo de
apertura  x . Si el archivo ya existe, la función  open()  lanzará una excepción.

Abriendo el stream por primera vez


Imagina que queremos desarrollar un programa que lea el contenido del archivo de texto
llamado: C:\Users\User\Desktop\file.txt.

¿Cómo abrir ese archivo para leerlo? Aquí está el fragmento del código:

try:

stream = open("C:\Users\User\Desktop\file.txt", "rt")

# aqui se procesa el archivo

stream.close()

except Exception as exc:

print("No se puede abrir el archivo:", exc)

¿Que está pasando aqui?

 Hemos abierto el bloque try-except ya que queremos manejar los errores de tiempo de
ejecución suavemente.
 Se emplea la función  open()  para intentar abrir el archivo especificado (ten en cuenta la
forma en que hemos especificado el nombre del archivo).
 El modo de apertura se define como texto para leer (como texto es la configuración
predeterminada, podemos omitir la  t  en la cadena de modo).
 En caso de éxito obtenemos un objeto de la función  open()  y lo asignamos a la variable
del stream.
 Si  open()  falla, manejamos la excepción imprimiendo la información completa del error
(es bueno saber qué sucedió exactamente).
Streams pre-abiertos
Dijimos anteriormente que cualquier operación del stream debe estar precedida por la invocación de
la función  open() . Hay tres excepciones bien definidas a esta regla.

Cuando comienza nuestro programa, los tres streams ya están abiertos y no requieren ninguna
preparación adicional. Además, tu programa puede usar estos streams explícitamente si tienes
cuidado de importar el módulo  sys :

import sys

Porque ahí es donde se coloca la declaración de estos streams.

Los nombres de los streams son:  sys.stdin ,  sys.stdout  y  sys.stderr .

Vamos a analizarlos:

 sys.stdin
o stdin (significa entrada estándar).
o El stream  stdin  normalmente se asocia con el teclado, se abre previamente para
la lectura y se considera como la fuente de datos principal para los programas en
ejecución.
o La función bien conocida  input()  lee datos de  stdin  por default.

 sys.stdout
o stdout (significa salida estándar).
o El stream  stdout  normalmente está asociado con la pantalla, preabierta para
escritura, considerada como el objetivo principal para la salida de datos por el
programa en ejecución.
o La función bien conocida  print()  envía los datos al stream  stdout .

 sys.stderr
o stderr (significa salida de error estándar).
o El stream  stderr  normalmente está asociado con la pantalla, preabierta para
escribir, considerada como el lugar principal donde el programa en ejecución debe
enviar información sobre los errores encontrados durante su trabajo.
o No hemos presentado ningún método para enviar datos a este stream (lo haremos
pronto, lo prometemos).
o La separación de  stdout  (resultados útiles producidos por el programa)
de  stderr  (mensajes de error, indudablemente útiles pero no proporcionan
resultados) ofrece la posibilidad de redirigir estos dos tipos de información a los
diferentes objetivos. Una discusión más extensa sobre este tema está más allá del
alcance de nuestro curso. El manual del sistema operativo proporcionará más
información sobre estos temas.

Cerrando streams
La última operación realizada en un stream (esto no incluye a los streams  stdin ,  stdout ,
y  stderr  pues no lo requieren) debe ser cerrarlo.

Esa acción se realiza mediante un método invocado desde dentro del objeto del
stream:  stream.close() .

 El nombre de la función es fácil de entender  close() , es decir cerrar.


 La función no espera argumentos; el stream no necesita estar abierto.
 La función no devuelve nada pero lanza una excepción IOError en caso de un error.
 La mayoría de los desarrolladores creen que la función  close()  siempre tiene éxito y, por
lo tanto, no hay necesidad de verificar si ha realizado su tarea correctamente.

Esta creencia está solo parcialmente justificada. Si el stream se abrió para escribir y luego se
realizó una serie de operaciones de escritura, puede ocurrir que los datos enviados al stream
aún no se hayan transferido al dispositivo físico (debido a los mecanismos
de cache o buffer). Dado que el cierre del stream obliga a los búferes a descargarse, es
posible que dichas descargas fallen y, por lo tanto,  close()  falle también.

Ya hemos mencionado fallas causadas por funciones que operan con los streams, pero no
mencionamos nada sobre cómo podemos identificar exactamente la causa de la falla.

La posibilidad de hacer un diagnóstico existe y es proporcionada por uno de los componentes de


excepción de los streams.

Diagnosticando problemas con los streams


El objeto  IOError  está equipado con una propiedad llamada  errno  (el nombre viene de la
frase error number, número de error) y puedes accederla de la siguiente manera:

try:

# operaciones con streams

except IOError as exc:

print(exc.errno)

El valor del atributo  errno  se puede comparar con una de las constantes simbólicas predefinidas en
módulo  errno .

Echemos un vistazo a algunas constantes seleccionadas útiles para detectar errores de flujo:

errno.EACCES  → Permiso denegado


El error se produce cuando intentas, por ejemplo, abrir un archivo con atributos de solo lectura para
abrirlo.

errno.EBADF  → Número de archivo incorrecto

El error se produce cuando intentas, por ejemplo, operar un stream sin abrirlo.

errno.EEXIST  → Archivo existente

El error se produce cuando intentas, por ejemplo, cambiar el nombre de un archivo con su nombre
anterior.

errno.EFBIG  → Archivo demasiado grande

El error ocurre cuando intentas crear un archivo que es más grande que el máximo permitido por el
sistema operativo.

errno.EISDIR  → Es un directorio

El error se produce cuando intentas tratar un nombre de directorio como el nombre de un archivo
ordinario.

errno.EMFILE  → Demasiados archivos abiertos

El error se produce cuando intentas abrir simultáneamente más streams de los aceptables para el
sistema operativo.

errno.ENOENT  → El archivo o directorio no existe

El error se produce cuando intentas acceder a un archivo o directorio inexistente.

errno.ENOSPC  → no queda espacio en el dispositivo

El error ocurre cuando no hay espacio libre en el dispositivo.

La lista completa es mucho más larga (incluye también algunos códigos de error no relacionados con
el procesamiento del stream).

Diagnosticando problemas con los streams:


continuación
Si eres un programador muy cuidadoso, puedes sentir la necesidad de usar una secuencia de
sentencias similar a la que se presenta a continuación:

import errno

try:

s = open("c:/users/user/Desktop/file.txt", "rt")

# el procesamiento va aquí

s.close()
except Exception as exc:

if exc.errno == errno.ENOENT:

print("El archivo no existe.")

elif exc.errno == errno.EMFILE:

print("Has abierto demasiados archivos.")

else:

printf("El número de error es:", exc.errno)

Afortunadamente, existe una función que puede simplificar el código de manejo de errores. Su


nombre es  strerror() , y proviene del módulo  os  y espera solo un argumento: un número de
error.

Su función es simple: proporciona un número de error y una cadena que describe el significado del
error.

Nota: Si pasas un código de error inexistente (un número que no está vinculado a ningún error real),
la función lanzará una excepción ValueError.

Ahora podemos simplificar nuestro código de la siguiente manera:

from os import strerror

try:

s = open("c:/users/user/Desktop/file.txt", "rt")

# el procesamiento va aquí

s.close()

except Exception as exc:

print("El archivo no se pudo abrir:", strerror(exc.errno));

Bueno. Ahora es el momento de tratar con archivos de texto y familiarizarse con algunas técnicas
básicas que puedes utilizar para procesarlos.

Procesamiento de archivos de texto


En esta lección vamos a preparar un archivo de texto simple con contenido breve y
simple.
Te mostraremos algunas técnicas básicas que puedes utilizar para leer el contenido
del archivo para poder procesarlo.

El procesamiento será muy simple: vas a copiar el contenido del archivo a la consola y
contarás todos los caracteres que el programa ha leído.

Pero recuerda: nuestra comprensión de un archivo de texto es muy estricta. Es un


archivo de texto sin formato: puede contener solo texto, sin decoraciones adicionales
(formato, diferentes fuentes, etc.).

Es por eso que debes evitar crear el archivo utilizando un procesador de texto
avanzado como MS Word, LibreOffice Writer o algo así. Utiliza los conceptos básicos
que ofrece tu sistema operativo: Bloc de notas, vim, gedit, etc.

Si tus archivos de texto contienen algunos caracteres nacionales no cubiertos por el


juego de caracteres ASCII estándar, es posible que necesites un paso adicional. La
invocación de tu función  open()  puede requerir un argumento que denote una
codificación específica del texto.

Por ejemplo, si estás utilizando un sistema operativo Unix/Linux configurado para usar
UTF-8 como una configuración de todo el sistema, la función  open()  puede verse de la
siguiente manera:

stream = open('file.txt', 'rt', encoding='utf-8')

Donde el argumento de codificación debe establecerse en un valor dentro de una


cadena que representa la codificación de texto adecuada (UTF-8, en este caso).

Consulta la documentación de tu sistema operativo para encontrar el nombre de


codificación adecuado para tu entorno.

INFORMACIÓ N

A los fines de nuestros experimentos con el procesamiento de archivos que se llevan a


cabo en esta sección, vamos a utilizar un conjunto de archivos precargados (p. Ej., los
archivos tzop.txt, o text.txt) con los cuales podrás trabajar. Si deseas trabajar con
tus propios archivos localmente en tu máquina, te recomendamos que lo hagas y que
utilices un Entorno de Desarrollo para llevar a cabo tus propias pruebas.

Procesamiento de archivos de texto: continuación


La lectura del contenido de un archivo de texto se puede realizar utilizando diferentes
métodos; ninguno de ellos es mejor o peor que otro. Depende de ti cuál de ellos
prefieres y te gusta.
Algunos de ellos serán a veces más prácticos y otros más problemáticos. Se flexible.
No tengas miedo de cambiar tus preferencias.

El más básico de estos métodos es el que ofrece la función  read() , la cual pudiste ver
en acción en la lección anterior.

Si se aplica a un archivo de texto, la función es capaz de:

 Leer un número determinado de caracteres (incluso solo uno) del archivo y


devolverlos como una cadena.
 Leer todo el contenido del archivo y devolverlo como una cadena.
 Si no hay nada más que leer (el cabezal de lectura virtual llega al final del
archivo), la función devuelve una cadena vacía.

Comenzaremos con la variante más simple y usaremos un archivo llamado  text.txt .


El archivo contiene lo siguiente:

Lo hermoso es mejor que lo feo.


Explícito es mejor que implícito.
Simple es mejor que complejo.
Complejo es mejor que complicado.

Ahora observa el código en el editor y analicémoslo.

from os import strerror

try:

cnt = 0

s = open('text.txt', "rt")

ch = s.read(1)

while ch != '':

print(ch, end='')

cnt += 1

ch = s.read(1)

s.close()

print("\n\nCaracteres en el archivo: ", cnt)


except IOError as e:

print("Se produjo un error de E/S: ", strerr(e.errno))

La rutina es bastante simple:

 Se usa el mecanismo try-except y se abre el archivo con el nombre


(text.txt en este caso).
 Intenta leer el primer caracter del archivo ( ch = s.read(1) ).
 Si tienes éxito (esto se demuestra por el resultado positivo de la
condición  while ), se muestra el caracter (nota el argumento  end= ,¡es
importante! ¡No querrás saltar a una nueva línea después de cada caracter!).
 Se actualiza el contador ( cnt ).
 Intenta leer el siguiente carácter y el proceso se repite.

Resultado:

Beautiful is better than ugly.


Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.

Caracteres en el archivo: 131

Procesamiento de archivos de texto: continuación


Si estás absolutamente seguro de que la longitud del archivo es segura y puedes leer
todo el archivo en la memoria de una vez, puedes hacerlo: la función  read() , invocada
sin ningún argumento o con un argumento que se evalúa a  None , hará el trabajo por
ti.

Recuerda - el leer un archivo muy grande (en terabytes) usando este método
puede dañar tu sistema operativo.

No esperes milagros: la memoria de la computadora no se puede extender.

Observa el código en el editor. ¿Que piensas de el?

from os import strerror

try:

cnt = 0

s = open('text.txt', "rt")

content = s.read()

for ch in content:
print(ch, end='')

cnt += 1

ch = s.read(1)

s.close()

print("\n\nCaracteres en el archivo: ", cnt)

except IOError as e:

print("Se produjo un error de E/S: ", strerr(e.errno))

Vamos a analizarlo:

 Abre el archivo, como anteriormente se hizo.


 Lee el contenido mediante una invocación de la función  read() .
 Despues, se procesa el texto, iterando con un bucle  for  su contenido, y se
actualiza el valor del contador en cada vuelta del bucle.

El resultado será exactamente el mismo que en el ejemplo anterior.

Resultado:

Beautiful is better than ugly.


Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.

Caracteres en el archivo: 131

Procesando archivos de texto: readline()


Si deseas manejar el contenido del archivo como un conjunto de líneas, no como un
montón de caracteres, el método  readline()  te ayudará con eso.

El método intenta leer una línea completa de texto del archivo, y la devuelve como
una cadena en caso de éxito. De lo contrario, devuelve una cadena vacía.

Esto abre nuevas oportunidades: ahora también puedes contar líneas fácilmente, no
solo caracteres.

Hagámos uso de ello. Observa el código en el editor.

from os import strerror

try:
ccnt = lcnt = 0

s = open('text.txt', 'rt')

line = s.readline()

while line != '':

lcnt += 1

for ch in line:

print(ch, end='')

ccnt += 1

line = s.readline()

s.close()

print("\n\nCaracteres en el archivo: ", ccnt)

print("Lineas en el archivo: ", lcnt)

except IOError as e:

print("Se produjo un error de E/S: ", strerr(e.errno))

Como puedes ver, la idea general es exactamente la misma que en los dos ejemplos
anteriores.

Resultado:

Beautiful is better than ugly.


Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.

Caracteres en el archivo: 131

Procesando archivos de texto: readlines()


Otro método, que maneja el archivo de texto como un conjunto de líneas, no como
caracteres, es  readlines() .
Cuando el método  readlines() , se invoca sin argumentos, intenta leer todo el
contenido del archivo y devuelve una lista de cadenas, un elemento por línea del
archivo.

Si no estás seguro de si el tamaño del archivo es lo suficientemente pequeño y no


deseas probar el sistema operativo, puedes convencer al método  readlines()  de
leer no más de un número especificado de bytes a la vez (el valor de retorno sigue
siendo el mismo, es una lista de una cadena).

Siéntete libre de experimentar con este código de ejemplo para entender cómo


funciona el método  readlines() .

s = open("text.txt")

print(s.readlines(20))

print(s.readlines(20))

print(s.readlines(20))

print(s.readlines(20))

s.close()

Resultado:

['Beautiful is better than ugly.\n']


['Explicit is better than implicit.\n']
['Simple is better than complex.\n']
['Complex is better than complicated.']

El tamaño máximo del búfer de entrada aceptado se pasa al método como


argumento.

Puedes esperar que  readlines()  procese el contenido del archivo de manera más
efectiva que  readline() , ya que puede ser invocado menos veces.

Nota: cuando no hay nada que leer del archivo, el método devuelve una lista vacía.
Úsalo para detectar el final del archivo.

Puedes esperar que al aumentar el tamaño del búfer mejore el rendimiento de


entrada, pero no hay una regla de oro para ello: intenta encontrar los valores óptimos
por ti mismo.

Observa el código en el editor. Lo hemos modificado para mostrarte cómo


usar  readlines() .
from os import strerror

try:

ccnt = lcnt = 0

s = open('text.txt', 'rt')

lines = s.readlines(20)

while len(lines) != 0:

for line in lines:

lcnt += 1

for ch in line:

print(ch, end='')

ccnt += 1

lines = s.readlines(10)

s.close()

print("\n\nCaracteres en el archivo: ", ccnt)

print("Lineas en el archivo: ", lcnt)

except IOError as e:

print("Se produjo un error de E/S: ", strerr(e.errno))

Hemos decidido usar un búfer de 15 bytes de longitud. No pienses que es una


recomendación.

Hemos utilizado ese valor para evitar la situación en la que la primera invocación
de  readlines()  consuma todo el archivo.

Queremos que el método se vea obligado a trabajar más duro y que demuestre sus
capacidades.

Existen dos bucles anidados en el código: el exterior emplea el resultado


de  readlines()  para iterar a través de él, mientras que el interno imprime las líneas
carácter por carácter.

Resultado:
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.

Caracteres en el archivo: 131


Lineas en el archivo: 4

Procesando archivos de texto: continuación


El último ejemplo que queremos presentar muestra un rasgo muy interesante del
objeto devuelto por la función  open()  en modo de texto.

Creemos que puede sorprenderte - el objeto es una instancia de la clase iterable.

¿Extraño? De ningúna manera. ¿Usable? Si, por supuesto.

El protocolo de iteración definido para el objeto del archivo es muy simple: su


método  __next__  solo devuelve la siguiente línea leída del archivo.

Además, puedes esperar que el objeto invoque automáticamente a  close()  cuando


cualquiera de las lecturas del archivo lleguen al final del archivo.

Mira el editor y ve cuán simple y claro se ha vuelto el código.

from os import strerror

try:

ccnt = lcnt = 0

for line in open('text.txt', 'rt'):

lcnt += 1

for ch in line:

print(ch, end='')

ccnt += 1

print("\n\nCaracteres en el archivo: ", ccnt)

print("Lineas en el archivo: ", lcnt)

except IOError as e:

print("Se produjo un error de E/S: ", strerr(e.errno))


Resultado:

Beautiful is better than ugly.


Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.

Caracteres en el archivo: 131


Lineas en el archivo: 4

Manejando archivos de texto: write()


Escribir archivos de texto parece ser más simple, ya que hay un método que puede
usarse para realizar dicha tarea.

El método se llama  write()  y espera solo un argumento: una cadena que se


transferirá a un archivo abierto (no lo olvides), el modo de apertura debe reflejar la
forma en que se transfieren los datos - escribir en un archivo abierto en modo de
lectura no tendrá éxito).

No se agrega carácter de nueva línea al argumento de  write() , por lo que debes
agregarlo tu mismo si deseas que el archivo se complete con varias líneas.

El ejemplo en el editor muestra un código muy simple que crea un archivo


llamado newtext.txt (nota: el modo de apertura  w  asegura que el archivo se creará
desde cero, incluso si existe y contiene datos) y luego pone diez líneas en él.

from os import strerror

try:

fo = open('newtext.txt', 'wt') #un nuevo archivo (newtext.txt) es creado

for i in range(10):

s = "línea #" + str(i+1) + "\n"

for ch in s:

fo.write(ch)

fo.close()

except IOError as e:

print("Se produjo un error de E/S: ", strerr(e.errno))


La cadena que se grabará consta de la palabra línea, seguida del número de línea.
Hemos decidido escribir el contenido de la cadena carácter por carácter (esto lo hace
el bucle interno  for ) pero no estás obligado a hacerlo de esta manera.

Solo queríamos mostrarte que  write()  puede operar con caracteres individuales.

El código crea un archivo con el siguiente texto:

línea #1

línea #2

línea #3

línea #4

línea #5

línea #6

línea #7

línea #8

línea #9

línea #10

¿Puedes imprimir el contenido del archivo en la consola?

Te alentamos a que pruebes el comportamiento del método  write()  localmente en


tu máquina.

Manejando archivos de texto: continuación


Mira el ejemplo en el editor. Hemos modificado el código anterior para escribir líneas
enteras en el archivo de texto.

from os import strerror

try:

fo = open('newtext.txt', 'wt')

for i in range(10):

fo.write("line #" + str(i+1) + "\n")

fo.close()

except IOError as e:
print("Se produjo un error de E/S: ", strerr(e.errno))

El contenido del archivo recién creado es el mismo.

Nota: puedes usar el mismo método para escribir en el stream  stderr , pero no
intentes abrirlo, ya que siempre está abierto implícitamente.

Por ejemplo, si deseas enviar un mensaje de tipo cadena a  stderr  para distinguirlo de
la salida normal del programa, puede verse así:

import sys

sys.stderr.write("Mensaje de Error")

¿Qué es un bytearray?
Antes de comenzar a hablar sobre archivos binarios, tenemos que informarte sobre una de las clases
especializadas que Python usa para almacenar datos amorfos.

Los datos amorfos son datos que no tienen forma específica - son solo una serie de bytes.

Esto no significa que estos bytes no puedan tener su propio significado o que no puedan representar
ningún objeto útil, por ejemplo, gráficos de mapa de bits.

Los datos amorfos no pueden almacenarse utilizando ninguno de los medios presentados
anteriormente: no son cadenas ni listas.

Debe haber un contenedor especial capaz de manejar dichos datos.

Python tiene más de un contenedor, uno de ellos es una clase especializada llamada bytearray -
como su nombre indica, es un arreglo que contiene bytes (amorfos).

Si deseas tener dicho contenedor, por ejemplo, para leer una imagen de mapa de bits y procesarla de
alguna manera, debes crearlo explícitamente, utilizando uno de los constructores disponibles.

Observa:

data = bytearray(100)

Tal invocación crea un objeto bytearray capaz de almacenar diez bytes.

Nota: dicho constructor llena todo el arreglo con ceros.

Bytearrays: continuación
Bytearrays se asemejan a listas en muchos aspectos. Por ejemplo, son mutables, son
suceptibles a la función  len() , y puedes acceder a cualquiera de sus elementos
usando indexación convencional.

Existe una limitación importante - no debes establecer ningún elemento del arreglo
de bytes con un valor que no sea un entero (violar esta regla causará una
excepción TypeError) y tampoco está permitido asignar un valor fuera del rango
de 0 a 255 (a menos que quieras provocar una excepción ValueError).

Puedes tratar cualquier elemento del arreglo de bytes como un valor entero - al


igual que en el ejemplo en el editor.

data = bytearray(10)

for i in range(len(data)):

data[i] = 10 - i

for b in data:

print(hex(b))

Nota: hemos utilizado dos métodos para iterar el arreglo de bytes, y hemos utilizado la
función  hex()  para ver los elementos impresos como valores hexadecimales.

Ahora te vamos a mostrar cómo escribir un arreglo de bytes en un archivo binario,


como no queremos guardar su representación legible, queremos escribir una copia
uno a uno del contenido de la memoria física, byte a byte.

Resultado:

0xa
0x9
0x8
0x7
0x6
0x5
0x4
0x3
0x2
0x1

Bytearrays: continuación
Entonces, ¿cómo escribimos un arreglo de bytes en un archivo binario?

Observa el código en el editor. Analicémoslo:

from os import strerror


data = bytearray(10)

for i in range(len(data)):

data[i] = 10 + i

try:

bf = open('file.bin', 'wb')

bf.write(data)

bf.close()

except IOError as e:

print("Se produjo un error de E/S: ", strerr(e.errno))

 Primero, inicializamos  bytearray  con valores a partir de  10 ; si deseas que el


contenido del archivo sea claramente legible, reemplaza el  10 con algo
como  ord('a') , esto producirá bytes que contienen valores correspondientes
a la parte alfabética del código ASCII (no pienses que harás que el archivo sea
un archivo de texto; sigue siendo binario, ya que se creó con un indicador -
bandera  wb ).
 Después, creamos el archivo usando la función  open() , la única diferencia en
comparación con las variantes anteriores es que el modo de apertura contiene
el indicador  b .
 El método  write()  toma su argumento ( bytearray ) y lo envía (como un todo)
al archivo.
 El stream se cierra de forma rutinaria.

El método  write()  devuelve la cantidad de bytes escritos correctamente.

Si los valores difieren de la longitud de los argumentos del método, puede significar
que hay algunos errores de escritura.

En este caso, no hemos utilizado el resultado; esto puede no ser apropiado en todos
los casos.

Intenta ejecutar el código y analiza el contenido del archivo recién creado.

Lo vas a usar en el siguiente paso.


Cómo leer bytes de un stream
La lectura de un archivo binario requiere el uso de un método especializado
llamado  readinto() , ya que el método no crea un nuevo objeto del arreglo de bytes,
sino que llena uno creado previamente con los valores tomados del archivo binario.

Nota:

 El método devuelve el número de bytes leídos con éxito.


 El método intenta llenar todo el espacio disponible dentro de su argumento; si
existen más datos en el archivo que espacio en el argumento, la operación de
lectura se detendrá antes del final del archivo; el resultado del método puede
indicar que el arreglo de bytes solo se ha llenado de manera fragmentaria (el
resultado también lo mostrará y la parte del arreglo que no está siendo
utilizada por los contenidos recién leídos permanece intacta).

Mira el código completo a continuación:

from os import strerror

data = bytearray(10)

try:

bf = open('file.bin', 'rb')

bf.readinto(data)

bf.close()

for b in data:

print(hex(b), end=' ')

except IOError as e:

print("Se produjo un error de E/S: ", strerr(e.errno))

Analicémoslo:

 Primero, abrimos el archivo (el que se creó usando el código anterior) con el
modo descrito como  rb .
 Luego, leemos su contenido en el arreglo de bytes llamado  data , con un
tamaño de diez bytes.
 Finalmente, imprimimos el contenido del arreglo de bytes: ¿Son los mismos
que esperabas?
Ejecuta el código y verifica si funciona.

Cómo leer bytes de un stream


Se ofrece una forma alternativa de leer el contenido de un archivo binario mediante el
método denominado  read() .

Invocado sin argumentos, intenta leer todo el contenido del archivo en la memoria,


haciéndolo parte de un objeto recién creado de la clase bytes.

Esta clase tiene algunas similitudes con  bytearray , con la excepción de una diferencia
significativa: es immutable.

Afortunadamente, no hay obstáculos para crear un arreglo de bytes tomando su valor


inicial directamente del objeto de bytes, como aquí:

from os import strerror

try:

bf = open('file.bin', 'rb')

data = bytearray(bf.read())

bf.close()

for b in data:

print(hex(b), end=' ')

except IOError as e:

print("Se produjo un error de E/S: ", strerr(e.errno))

Ten cuidado - no utilices este tipo de lectura si no estás seguro de que el
contenido del archivo se ajuste a la memoria disponible.

Cómo leer bytes de un stream: continuación


Si el método  read()  se invoca con un argumento, se especifica el número máximo
de bytes a leer.

El método intenta leer la cantidad deseada de bytes del archivo, y la longitud del
objeto devuelto puede usarse para determinar la cantidad de bytes realmente leídos.
Puedes usar el método como aquí:

try:

bf = open('file.bin', 'rb')

data = bytearray(bf.read(5))

bf.close()

for b in data:

print(hex(b), end=' ')

except IOError as e:

print("Se produjo un error de E/S:", strerr(e.errno))

Nota: los primeros cinco bytes del archivo han sido leídos por el código; los siguientes
cinco todavía están esperando ser procesados.

Copiando archivos: una herramienta simple y


funcional
Ahora vas a juntar todo este nuevo conocimiento, agregarle algunos elementos
nuevos y usarlo para escribir un código real que pueda copiar el contenido de un
archivo.

Por supuesto, el propósito no es crear un reemplazo para los comandos como copy de


(MS Windows) o cp de (Unix/Linux) pero para ver una forma posible de crear una
herramienta de trabajo, incluso si nadie quiere usarla.

Observa el código en el editor. Analicémoslo:

from os import strerror

srcname = input("¿Nombre del archivo fuente?: ")

try:

src = open(srcname, 'rb')

except IOError as e:

print("No se puede abrir archivo fuente: ", strerror(e.errno))

exit(e.errno)
dstname = input("¿Nombre del archivo de destino?: ")

try:

dst = open(dstname, 'wb')

except Exception as e:

print("No se puede crear el archivo de destino: ", strerr(e.errno))

src.close()

exit(e.errno)

buffer = bytearray(65536)

total = 0

try:

readin = src.readinto(buffer)

while readin > 0:

written = dst.write(buffer[:readin])

total += written

readin = src.readinto(buffer)

except IOError as e:

print("No se puede crear el archivo de destino: ", strerr(e.errno))

exit(e.errno)

print(total,'byte(s) escritos con éxito')

src.close()

dst.close()

 Las líneas 3 a la 8: solicitan al usuario el nombre del archivo a copiar e intentan


abrirlo para leerlo; se termina la ejecución del programa si falla la apertura;
nota: emplea la función  exit()  para detener la ejecución del programa y
pasar el código de finalización al sistema operativo; cualquier código de
finalización que no sea  0  significa que el programa ha encontrado algunos
problemas; se debe utilizar el valor  errno  para especificar la naturaleza del
problema.
 Las líneas 9 a la 15: repiten casi la misma acción, pero esta vez para el archivo
de salida.
 La línea 17: prepara una parte de memoria para transferir datos del archivo
fuente al destino; Tal área de transferencia a menudo se llama un búfer, de ahí
el nombre de la variable; el tamaño del búfer es arbitrario; en este caso,
decidimos usar 64 kilobytes; técnicamente, un búfer más grande es más rápido
al copiar elementos, ya que un búfer más grande significa menos operaciones
de E/S; en realidad, siempre hay un límite, cuyo cruce no genera más ventajas;
pruébalo tú mismo si quieres.
 Línea 18: cuenta los bytes copiados: este es el contador y su valor inicial.
 Línea 20: intenta llenar el búfer por primera vez.
 Línea 21: mientras se obtenga un número de bytes distinto de cero, repite las
mismas acciones.
 Línea 22: escribe el contenido del búfer en el archivo de salida (nota: hemos
usado un segmento para limitar la cantidad de bytes que se escriben, ya
que  write()  siempre prefiero escribir todo el búfer).
 Línea 23: actualiza el contador.
 Línea 24: lee el siguiente fragmento de archivo.
 Las líneas 29 a la 31: limpieza final: el trabajo está hecho.

 6.1.9.15 LABORATORIO: Histograma de frecuencia de caracteres

LABORATORIO

Tiempo Estimado
30 minutos

Nivel de dificultad
Medio

Objetivos
 Mejorar las habilidades del estudiante al operar con la lectura archivos.
 Utilizar colecciones de datos para contar datos numerosos.

Escenario
Un archivo de texto contiene algo de texto (nada inusual) pero necesitamos saber con
qué frecuencia aparece cada letra en el texto. Tal análisis puede ser útil en criptografía,
por lo que queremos poder hacerlo en referencia al alfabeto latino.

Tu tarea es escribir un programa que:

 Pida al usuario el nombre del archivo de entrada.


 Lea el archivo (si es posible) y cuente todas las letras latinas (las letras
mayúsculas y minúsculas se tratan como iguales).
 Imprima un histograma simple en orden alfabético (solo se deben presentar
recuentos distintos de cero).

Crea un archivo de prueba para tu código y verifica si tu histograma contiene


resultados válidos.

Suponiendo que el archivo de prueba contiene solo una línea con:

aBc

el resultado esperado debería verse de la siguiente manera: a -> 1

b -> 1

c -> 1

Tip:

Creemos que un diccionario es un medio perfecto de recopilación de datos para


almacenar los recuentos. Las letras pueden ser las claves mientras que los contadores
pueden ser los valores.

 6.1.9.16 LABORATORIO: Histograma de frecuencia de caracteres


ordenado

LABORATORIO

Tiempo Estimado
15-20 minutos

Nivel de dificultad
Medio

Prerrequisitos
05_9.15.1
Objetivos
 Mejorar las habilidades del estudiante para operar con archivos en modo
(lectura/escritura).
 Emplear lambdas para cambiar el ordenamiento.

Escenario
El código anterior necesita ser mejorado. Está bien, pero tiene que ser mejor.

Tu tarea es hacer algunas enmiendas, que generen los siguientes resultados:

 El histograma de salida se ordenará en función de la frecuencia de los


caracteres (el contador más grande debe presentarse primero).
 El histograma debe enviarse a un archivo con el mismo nombre que el de
entrada, pero con la extensión '.hist' (debe concatenarse con el nombre
original).

Suponiendo que el archivo de prueba contiene solo una línea con:

cBabAa

El resultado esperado debería verse de la siguiente manera: a -> 3

b -> 2

c -> 1

Tip:

Emplea una  lambda  para cambiar el ordenamiento.

 6.1.9.17 LABORATORIO: Evaluando los resultados de los


estudiantes

LABORATORIO

Tiempo Estimado
30 minutos

Nivel de dificultad
Medio
Objetivos
 Mejorar las habilidades del alumno para operar con archivos en modo lectura.
 Perfeccionar las habilidades del estudiante para definir y usar excepciones y
diccionarios autodefinidos.

Escenario
El profesor Jekyll dirige clases con estudiantes y regularmente toma notas en un
archivo de texto. Cada línea del archivo contiene 3 elementos: el nombre del alumno,
el apellido del alumno y el número de puntos que el alumno recibió durante ciertas
clases.

Los elementos están separados con espacios en blanco. Cada estudiante puede
aparecer más de una vez dentro del archivo del profesor Jekyll.

El archivo puede tener el siguiente aspecto:

John Smith 5

Anna Boleyn 4.5

John Smith 2

Anna Boleyn 11

Andrew Cox 1.5

Tu tarea es escribir un programa que:

 Pida al usuario el nombre del archivo del profesor Jekyll.


 Lea el contenido del archivo y cuenta la suma de los puntos recibidos para cada
estudiante.
 Imprima un informe simple (pero ordenado), como este:
Andrew Cox 1.5

Anna Boleyn 15.5

John Smith 7.0

Nota:

 Tu programa debe estar completamente protegido contra todas las fallas


posibles: la inexistencia del archivo, el vacío del archivo o cualquier falla en los
datos de entrada; encontrar cualquier error de datos debería causar la
terminación inmediata del programa, y lo erróneo deberá presentarse al
usuario.
 Implementa y usa tu propia jerarquía de excepciones: la presentamos en el
editor; la segunda excepción se debe generar cuando se detecta una línea
incorrecta y la tercera cuando el archivo fuente existe pero está vacío.
Tip:

Emplea un diccionario para almacenar los datos de los estudiantes.

class ExcepcionDatosAlumnos(Exception):
pass

class LineaErronea(ExcepcionDatosAlumnos):
# coloca tu código aquí

class ArchivoVacio(ExcepcionDatosAlumnos):
# coloca tu código aquí

También podría gustarte