Está en la página 1de 798

CISCO

Python Essentials Parte 1


Recursos de Estudio
Bienvenido a Fundamentos de Programación en Python - Parte 1
Módulo 1
Introducción a Python y a la programación.

Módulo 2
Tipos de datos, variables, operaciones básicas de entrada y salida, operadores
básicos.

Módulo 3
Valores booleanos, ejecución condicional, bucles, listas y procesamiento de
listas, operaciones lógicas y bit a bit.

Módulo 4
Funciones, tuplas, diccionarios y procesamiento de datos.
Módulo 1

Fundamentos de Programación en Python: Módulo 1

En este módulo, aprenderás sobre:

 Fundamentos de programación.
 Establecimiento de tu entorno de programación.
 Compilación vs. interpretación.
 Introducción a Python.
Módulo 1

¿Cómo funciona un programa de computadora?


Este curso tiene como objetivo explicar el lenguaje Python y para que se utiliza. Vamos a
comenzar desde los fundamentos básicos.

Un programa hace que una computadora sea utilizable. Sin un programa, una computadora,
incluso la más poderosa, no es más que un objeto. Del mismo modo, sin un pianista, un piano
no es más que una caja de madera.

Las computadoras pueden realizar tareas muy complejas, pero esta habilidad no es innata.
La naturaleza de una computadora es bastante diferente.

Una computadora puede ejecutar solo operaciones extremadamente simples, por ejemplo,
una computadora no puede evaluar el valor de una función matemática complicada por sí
misma, aunque esto no está más allá de los límites posibles en un futuro próximo.
Las computadoras contemporáneas solo pueden evaluar los
resultados de operaciones muy fundamentales, como sumar
o dividir, pero pueden hacerlo muy rápido y pueden repetir
estas acciones prácticamente cualquier cantidad de veces.

Imagina que quieres saber la velocidad promedio que has


alcanzado durante un largo viaje. Sabes la distancia, sabes el
tiempo, necesitas la velocidad.

Naturalmente, la computadora podrá calcular esto, pero la computadora no es consciente


de cosas como la distancia, la velocidad o el tiempo. Por lo tanto, es necesario instruir a la
computadora para que:
Módulo 1
 Acepte un número que represente la distancia.
 Acepte un número que represente el tiempo de viaje.
 Divida el valor anterior por el segundo y almacene el resultado en la memoria.
 Muestre el resultado (representando la velocidad promedio) en un formato legible.

Estas cuatro acciones simples forman un programa. Por supuesto, estos ejemplos no están
formalizados, y están muy lejos de lo que la computadora puede entender, pero son lo
suficientemente buenos como para traducirlos a un idioma que la computadora pueda
aceptar.
La palabra clave es el lenguaje.

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.
Módulo 1
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.

¿Qué hace a un lenguaje?


Podemos decir que cada idioma (máquina o natural, no importa) consta de los siguientes
elementos:
ALFABETO
Módulo 1
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.
Módulo 1
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.

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.
Módulo 1
 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.
Módulo 1
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á?

¿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.
Módulo 1
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.
Módulo 1
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.
Módulo 1

Compilación vs. Interpretación - Ventajas y Desventajas


COMPILACIÓN INTERPRETACIÓN

Puede ejecutar el código en cuanto lo


 La ejecución del código
complete; no hay fases adicionales de
traducido suele ser más rápida.
 Solo el usuario debe tener el traducción.
compilador; el usuario final puede El código se almacena utilizando el lenguaje
usar el código sin él.
de programación, no el de la máquina; esto
VENTAJAS  El código traducido se almacena
significa que puede ejecutarse en
en lenguaje máquina, ya que es
computadoras que utilizan diferentes
muy difícil de entender, es
probable que tus propios inventos lenguajes máquina; no compila el código
y trucos de programación sigan por separado para cada arquitectura
siendo secreto. diferente.

La compilación en sí misma No esperes que la interpretación incremente



puede llevar mucho tiempo; es tu código a alta velocidad: tu código
posible que no puedas ejecutar tu compartirá la potencia de la computadora
código inmediatamente después con el intérprete, por lo que no puede ser
DESVENTAJAS de cualquier modificación.
realmente rápido.
 Tienes que tener tantos
compiladores como plataformas Tanto tú como el usuario final deben tener el
de hardware en los que deseas intérprete para ejecutar su código.
que se ejecute su código.
Módulo 1
¿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
fuentes codificadas que los usan se llaman scripts.

¿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.
Módulo 1
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.

Un proyecto de programación por hobby


Las circunstancias en las que se creó Python son un poco desconcertantes. Según Guido van
Rossum:
Módulo 1
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.
Módulo 1

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


Módulo 1
 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.

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.
Módulo 1
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 más 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:
Módulo 1
 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.

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
Módulo 1
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 más 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
Módulo 1
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.

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
Módulo 1
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).
Módulo 1

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
Módulo 1
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.

¿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
Módulo 1
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/.
Módulo 1

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.
Módulo 1
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.

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.
Módulo 1
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. Esto es lo que deberías ver:

¿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.
Módulo 1

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.
Módulo 1

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.

¿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...")
Módulo 1

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.
Módulo 1

¿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.
Módulo 1
Tu código debería parecerse al siguiente:

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.

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


Mira todas las ventanas con cuidado.
Módulo 1
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.
Módulo 1

¿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.
Módulo 1

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í.
Módulo 1
 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.

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:
Módulo 1
 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.

Interfaz de práctica
Este curso contiene cuatro tipos diferentes de interfaces.
Módulo 1
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.
print("¡Bienvenido a Fundamentos de Programación en Python!")

¡Felicidades! Has completado el Módulo 1


¡Bien hecho! Has llegado al final del Módulo 1 y has completado una meta importante en tu
educación de programación en Python. Aquí hay un breve resumen de los objetivos que has
cubierto y con los que te has familiarizado en el Módulo 1:
Los fundamentos de la programación de computadoras, es decir, como funciona la
computadora, como se ejecuta el programa, como se define y construye el lenguaje de
programación.
Módulo 1
La diferencia entre compilación e interpretación.

La información básica sobre Python y cómo se posiciona entre otros lenguajes de


programación, y qué distingue a sus diferentes versiones.
Los recursos de estudio y los diferentes tipos de interfaces que utilizarás en el curso.

Ahora estás listo para tomar el cuestionario del módulo, que te ayudará a evaluar lo que has
aprendido hasta ahora.
Módulo 2

Fundamentos de Programación en Python: Módulo 2

En este módulo, aprenderás sobre:

 Los tipos de datos y los métodos básicos de formateo, conversión de datos, entrada y
salida de datos.
 Operadores.
 Variables.
Módulo 2

¡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
qué 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.
Módulo 2
Cada uno de los elementos anteriores juega un papel muy importante en el código.

print("¡Hola, Mundo!")

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.
Módulo 2
 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.
Como se dijo anteriormente, una función puede tener:

 Un efecto.
Módulo 2
 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?


Módulo 2
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.
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!")
Módulo 2
¿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.
Módulo 2

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.
Módulo 2
 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.
Módulo 2

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.


Módulo 2

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.
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:
Módulo 2
 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).

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


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

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ó.
Módulo 2
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ó.") 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 tú 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.
Módulo 2
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ó.")
Módulo 2

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?
Módulo 2
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.")

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.")
Módulo 2

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).
Módulo 2

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 cómo 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.")

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.
Módulo 2
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 tú mismo.

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

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")
Módulo 2

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().


Módulo 2

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

print("Fundamentos","Programación","en")

print("Python")
Módulo 2

LABORATORIO

Tiempo Estimado
5-10 minutos

Nivel de dificultad
Fácil

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:
Módulo 2
 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(" *****")
Módulo 2

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().


Módulo 2

Resultado Esperado
Fundamentos***Programación***en...Python
print("Fundamentos","Programación","en")
print("Python")
Módulo 2

LABORATORIO

Tiempo Estimado
5-10 minutos

Nivel de dificultad
Fácil

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:
Módulo 2
 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(" *****")
Módulo 2

Literales - los datos en sí 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:
c
¿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.


Módulo 2
Se utilizan literales para codificar datos y ponerlos dentro del código. Ahora mostraremos
algunas convenciones que se deben seguir al utilizar Python.
Comencemos con un sencillo experimento, observa el fragmento de código en el editor.

La primera línea luce familiar. La segunda parece ser errónea debido a la falta visible de
comillas.
Intenta ejecutarlo.
Si todo salió bien, ahora deberías de ver dos líneas idénticas.
¿Qué paso? ¿Qué significa?
A través de este ejemplo, encuentras dos tipos diferentes de literales:
 Una cadena, la cual ya conoces.
 Y un número entero, algo completamente nuevo.
La función print() los muestra exactamente de la misma manera. Sin embargo,
internamente, la memoria de la computadora los almacena de dos maneras
completamente diferentes. La cadena existe como eso, solo una cadena, una serie de letras.

El número es convertido a una representación maquina (una serie de bits). La


función print() es capaz de mostrar ambos en una forma legible para humanos.

Vamos a tomar algo de tiempo para discutir literales numéricas y su vida interna.

print("2")
print(2)
Módulo 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.
Módulo 2
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.
Módulo 2

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.
Módulo 2
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:
Módulo 2
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 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.
Módulo 2
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:
Módulo 2
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.
Módulo 2
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"')
Módulo 2
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
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 más - una cadena
puede estar vacía - puede no contener carácter alguno.
Una cadena vacía sigue siendo una cadena:
''
""
Módulo 2

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:
Módulo 2
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?
Módulo 2

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.
Módulo 2

Salida Esperada
"Estoy"
""aprendiendo""
"""Python"""
Salida
Módulo 2

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).
Módulo 2
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

Ejercicio 2
¿Qué tipo de literales son los siguientes cuatro ejemplos?
"1.5", 2.0, 528, False
Revisar
Es 11, porque (2**0) + (2**1) + (2**3) = 11
Ejercicio 3
¿Cuál es el valor en decimal del siguiente número en binario?
1011
Módulo 2
Revisar

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)
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.
Módulo 2
Por ejemplo, como en la aritmética, el signo de + (mas) es un operador el cual es capaz
de sumar dos números, dando el resultado de la suma.

Sin embargo, no todos los operadores de Python son tan simples como el signo de más,
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.


Módulo 2
Nota: En los ejemplos, los dobles asteriscos están rodeados de espacios, no es obligatorio
hacerlo pero hace que el código sea más 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.

print(2 ** 3)
print(2 ** 3.)
print(2. ** 3)
print(2. ** 3.)

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)
Módulo 2
print(2 * 3.)
print(2. * 3)
print(2. * 3.)

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) print(6 / 3.) print(6. / 3) print(6. / 3.)
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:
Módulo 2
 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) print(6 // 3.) print(6. // 3) print(6. // 3.)
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 más avanzadas.
Observa el siguiente fragmento de código:
print(6 // 4) print(6. // 4)
Imagina que se utilizó / en lugar de // - ¿Podrías predecir los resultados?

Si, sería 1.5 en ambos casos. Eso está 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 más cercano del
resultado de la división no redondeada.
Esto es muy importante: el redondeo siempre va hacia abajo.
Módulo 2
Observa el código e intenta predecir el resultado nuevamente:
print(-6 // 4) print(6. // -4)
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 división 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 cómo 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.
Módulo 2
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 más 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)
Módulo 2

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 está 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.
Módulo 2
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) print(4. - 8) print(-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.
Módulo 2
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 cálculo de la expresión es realizado de izquierda a derecha.
Este simple ejemplo te mostrará cómo funciona. Observa:
print(9 % 6 % 2)
Módulo 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)
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?


Módulo 2
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)
Módulo 2
Ambos operadores (* y %) tienen la misma prioridad, el resultado solo se puede obtener
conociendo el sentido del enlazado. ¿Cuál será el resultado?

Revisar
1

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
Módulo 2

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 número 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.
Módulo 2
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
Módulo 2

¿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 cómo 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:
Módulo 2
 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, ElNombreEsTanL
argoQueSeCometeranErroresConEl, _.
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:
Módulo 2
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
Módulo 2
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 más de los cuales aún 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
Módulo 2
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
1
Módulo 2

Utilizando variables
Se tiene permitido utilizar cuantas declaraciones de variables sean necesarias para lograr el
objetivo del programa, por ejemplo:
var = 1 print(var, balance_cuenta,
nombreCliente)
balance_cuenta = 1000.0
print(var)
nombreCliente = 'John Doe'
Sin embargo, no se permite utilizar una variable que no exista, (en otras palabras, una variable
a la cual no se le ha 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?
Módulo 2
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 primera 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.
Módulo 2
De manera más 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 más 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.
Módulo 2

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
triángulo rectángulo, el opuesto al ángulo recto) utilizando el Teorema de Pitágoras:
a = 3.0 c = (a ** 2 + b ** 2) ** 0.5
b = 4.0 print("c =", c)

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

√ (x) = x(½)

c = √ a2 + b2

¿Puedes predecir la salida del código?


Revisa abajo y ejecuta el código en el editor para confirmar tus predicciones.
Módulo 2
Revisar
c = 5.0
a = 3.0
b = 4.0
c = (a ** 2 + b ** 2) ** 0.5
print("c =", c)
Módulo 2

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:
Módulo 2
 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 .

Operadores Abreviados
Es tiempo de explicar el siguiente conjunto de operadores que harán la vida del
programador/desarrollador más 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
Módulo 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 j = j - (i + var + rem) ⇒ j -= (i +
var + rem)
var = var / 2 ⇒ var /= 2
x = x ** 2 ⇒ x **= 2
rem = rem % 10 ⇒ rem %= 10
Módulo 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.
Módulo 2
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 está ocurriendo dentro de la función print(). Analiza cómo
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.
Módulo 2

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 = ###
kilometros_a_millas = ###
print(millas, " millas son ", round(millas_a_kilometros, 2), " kilómetros ")
print(kilometros, " kilómetros son ", round(kilometros_a_millas, 2), " millas ")
Módulo 2

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
Módulo 2
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 explícita. 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 = float(x)
# escribe tu código aquí.
print("y =", y)
Módulo 2

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 var = 3 var += 1
print(var) print(var) 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:
Módulo 2
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
3
Ejercicio 2
¿Cuáles de los siguientes nombres de variables son ilegales en Python?
my_var averylongvariablename Del
m m101 Del
101 m 101

Revisar
my_var
m
101 # incorrecto (comienza con un digito)
averylongvariablename
m101
m 101 # incorrecto (contiene un espacio)
Módulo 2
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

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 cómo es
que funciona, o tal vez especificar el significado de las variables, también para documentar
quien es el autor del programa y en qué fecha fue escrito.
Módulo 2
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)
Módulo 2
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 está probando un programa, con el fin de aislar
un fragmento de código donde posiblemente se encuentra un error.
Módulo 2

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.
Módulo 2
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 = 2 # 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 horaa
Módulo 2
#aquí también se debe de imprimir un "Adiós", pero el programador no tuvo tiempo
de escribirlo
#este el es fin del programa que calcula el numero de segundos en 2 horas
Módulo 2

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.
Módulo 2
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") Revisar
print("Cadena #2") Cadena #2

Ejercicio 2
¿Qué ocurrirá cuando se ejecute el siguiente código?
# Esto es en varias líneas #
un comentario print("Hola!")

Revisar
SyntaxError: invalid syntax

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

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


Módulo 2
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 cómo utilizar la función input().

Nota:
Módulo 2
 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?")
Módulo 2
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)
Módulo 2

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: ")
Módulo 2
resultado = algo ** 2.0
print(algo, "al cuadrado es ", resultado)

Conversió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.


Módulo 2
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á cómo 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.
Módulo 2
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)
Módulo 2

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.
Módulo 2
No se pueden mezclar los tipos de datos aquí.

Este es un programa sencillo que muestra cómo 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(), aun 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 número de veces indicado por el número.
Por ejemplo:
 "James" * 3 nos da "JamesJamesJames".
Módulo 2
 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 cómo 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 número 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():


Módulo 2
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.
Módulo 2

Conversión de tipos de datos: str()


A estas alturas ya sabes cómo 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.
Módulo 2
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.
Módulo 2

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.
Módulo 2
Prueba tu código - ¿Produce los resultados esperados?

# ingresa un valor flotante para la variable a aquí


# ingresa un valor flotante para la variable b aquí
# muestra el resultado de la suma aquí
# muestra el resultado de la resta aquí
# muestra el resultado de la multiplicación aquí
# muestra el resultado de la división aquí
print("\n¡Eso es todo, amigos!")
Módulo 2

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:
Módulo 2
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.

Datos de Prueba
Entrada de muestra: 1

Salida esperada:
y = 0.6000000000000001
Entrada de muestra: 10

Salida esperada:
y = 0.09901951266867294
Entrada de muestra: 100

Salida esperada:
y = 0.009999000199950014
Entrada de muestra: -5

Salida esperada:
Módulo 2
y = -0.19258202567760344
x = float(input("Ingresa el valor para x: "))
# coloca tu código aquí
print("y =", y)
Módulo 2

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.
Módulo 2
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 inválida, 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

1 2939
Salida esperada: 1:0

hora = int(input("Hora de inicio (horas): ")


min = int(input("Minuto de inicio (minutos): "))
Módulo 2
dura = int(input("Duración del evento (minutos): "))
# coloca tu código aquí
Módulo 2

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:
Módulo 2
nombre = input("Ingresa tu nombre: ")
print("Hola, " + nombre + ". ¡Un gusto conocerte!")
print("\nPresiona la tecla Enter para finalizar el programa.")
input()
print("FIN.")
4. 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
5. 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")
Módulo 2
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'>

¡Felicidades! Has completado el Módulo 2


¡Bien hecho! Has llegado al final del Módulo 2 y has completado un paso importante en tu
educación de programación en Python. Aquí hay un breve resumen de los objetivos que has
cubierto y con los que te has familiarizado en el Módulo 2:

 Los métodos básicos de formateo y salida de datos ofrecidos por Python, junto con los
tipos principales de datos y operadores numéricos, sus relaciones mutuas y enlaces.
 El concepto de variables y la manera correcta de darles nombre.
 El operador de asignación, las reglas que rigen la construcción de expresiones.
 La entrada y conversión de datos.
Módulo 2
Ahora estás listo para tomar el cuestionario del módulo e intentar el desafío final: La Prueba
del Módulo 2, que te ayudará a evaluar lo que has aprendido hasta ahora.
Módulo 3

Fundamentos de Programación en Python: Módulo 3

En este módulo, aprenderás sobre:


 Valores booleanos.
 Instrucciones if-elif-else.
 Bucles while y for.
 Control de flujo.
 Operaciones lógicas y bit a bit.
 Listas y arreglos.
Módulo 3

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:

 Sí, 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.
Módulo 3
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
Módulo 3
Esto debería ser fácil. La respuesta será (o mejor dicho, siempre es) False.

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 del operador == , la pregunta será tratada como la siguiente:
Módulo 3
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 var = 1 # asignando 1 a var
print(var == 0) 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 var = 1 # asignando 1 a var
print(var != 0) print(var != 0)
Ejecuta el código y comprueba si tenías razón.

Operadores de Comparación: Mayor que


También se puede hacer una pregunta de comparación usando el operador > (mayor que).
Módulo 3
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).
Módulo 3
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:
Módulo 3

Prioridad Operador

1 +, - unario

2 **
3 *, /, %
4 +, - binario

5 < , <= , > , >=


6 == , !=
Módulo 3

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.
Módulo 3

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


Módulo 3

Condiciones y ejecución condicional


Ya sabes cómo hacer preguntas a Python, pero aún no sabes cómo 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).
Módulo 3
 Unos dos puntos seguidos 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 más 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.
Módulo 3
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:
Módulo 3
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.
Módulo 3
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: else:
ejecuta_si_condición_true 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:
Módulo 3
if climaEsBueno: else: almorzar()
irACaminar() irAlCine()
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: else:
disfrutaLaPelicula()
irACaminar() irAlCine()
almorzar()
Diviertirse()

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:
Módulo 3
if encontramosBuenRestaurante:
almorzar()
else:
comerSandwich()
else:
if hayBoletosDisponibles:
irAlCine()
else:
irDeCompras()
Aquí hay dos puntos importantes:

 Este uso de la declaraciónif 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.
Módulo 3
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: elif mesasLibres:
iraCaminar() almorzar()
elif hayBoletosDisponibles: else:
IralCine() 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.


Módulo 3
 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
Módulo 3
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
Módulo 3
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
Módulo 3
#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.
Módulo 3

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.
Módulo 3
Veamos nuestro pseudocódigo a continuación:
línea 01 numeroMayor = -999999999 línea 05 exit()
línea 02 numero = int(input()) línea 06 if numero > numeroMayor:
línea 03 if numero == -1: línea 07 numeroMayor = numero
línea 04 print(numeroMayor) 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.
Módulo 3
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)
Módulo 3
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 código. Entonces, por ahora, no estamos tomando atajos.
Módulo 3

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.
Módulo 3
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!
Módulo 3
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:
Módulo 3
 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


Módulo 3
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:"))


#
# Coloca tu código aquí.
#
impuesto=round(impuesto, 0)
print("El impuesto es: ", impuesto, "pesos")
Módulo 3

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.
Módulo 3
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


Módulo 3
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:"))


#
# Coloca tu código aquí.
#
Módulo 3

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 Ejemplo
Devuelve si los valores de los operandos son iguales, x == y # False
==
y False de lo contrario. x == z # True
Devuelve True si los valores de los operandos no son x != y # True
!=
iguales, y False de lo contrario. x != z # False
DevuelveTrue si el valor del operando izquierdo es
x > y # False
> mayor que el valor del operando derecho,
y > z # True
y False de lo contrario.
Devuelve True si el valor del operando izquierdo es
x < y # True y
< menor que el valor del operando derecho,
< z # False
y False de lo contrario.
Devuelve True si el valor del operando izquierdo es x >= y # False
≥ mayor o igual al valor del operando derecho, x >= z # True
y False de lo contrario. y >= z # True
Devuelve True si el valor del operando izquierdo es x <= y # True
≤ menor o igual al valor del operando derecho, x <= z # True
y False de lo contrario. y <= z # False
2. Cuando desea ejecutar algún código solo si se cumple una determinada condición,
puede usar una declaración condicional:
Módulo 3
 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
Módulo 3
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 > 10: # Falso
if x > 5: # Verdadero print("x > 10")
print("x > 5") else:
if x > 8: # Verdadero print("Se ejecutará el else")
print("x > 8")
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 print("x > 10")
if x == 10: # Verdadero elif x > 5: # Verdadero
print("x == 10") print("x > 5")
if x > 15: # Falso else:
print("x > 15") print("No se ejecutará el else")
elif x > 10: # Falso
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:


Módulo 3
x = 10 print("anidado: x == 10")
if x > 5: # Verdadero else:
if x == 6: # Falso print("anidado: else")
print("anidado: x == 6") else:
elif x == 10: # Verdadero print("else")

Puntos Clave: Continuación


Ejercicio 1
¿Cuál es la salida del siguiente fragmento de código?
x = 5 z = 8 print(y > z)
y = 10 print(x > y)

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
Módulo 3
Ejercicio 3
¿Cuál es la salida del siguiente fragmento de código?
x, y, z = 5, 10, 8 print(x > z)
x, y, z = z, y, x print((y - 5) == x)

Revisar
True False

Ejercicio 4
¿Cuál es la salida del siguiente fragmento de código?
x = 10 if x > 5: print(x < 10)
if x == 10: print(x > 5) else:
print(x == 10) if x < 10: print("else")
Revisar
True True else

Ejercicio 5
¿Cuál es la salida del siguiente fragmento de código?
x = "1" print("dos") if int (x) == 1:
if x == 1: elif int (x) < 1: print("cinco")
print("uno") print("tres") else:
elif x == "1": else: print("seis")
if int (x)> 1: print("cuatro")
Módulo 3
Revisar
cuatro cinco

Ejercicio 6
¿Cuál es la salida del siguiente fragmento de código?
x = 1 print("uno") print("tres")
y = 1.0 if y == int (z): else:
z = "1" print("dos") print("cuatro")
if x == y: elif x == y:

Revisar
uno dos

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
Módulo 3
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 instrucción_uno :
expresión_condicional 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).
Módulo 3
 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.
Módulo 3
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
Módulo 3
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
Módulo 3
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:
Módulo 3

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:
Módulo 3

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. Mantén tu
código listo para un nuevo programador.
Módulo 3
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.
Módulo 3
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(
"""

+==================================+
Módulo 3
| 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? |
+==================================+

""")
Módulo 3

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 # hacer_algo()
while i < 100: 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.
Módulo 3
 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).
Módulo 3
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 ).
Módulo 3
¿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 cuál 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)
Módulo 3
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.
for i in range(2, 8, 3):
print("El valor de i es actualmente", i)
Módulo 3
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!
Módulo 3
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
Módulo 3
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 3 Mississippi 5 Mississippi
2 Mississippi 4 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.
Módulo 3

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.


Módulo 3
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 # continua - ejemplo
print("La instrucción de ruptura:") print("\nLa instrucción continue:")
for i in range(1,6): for i in range(1,6):
if i == 3: if i == 3:
break continue
print("Dentro del ciclo.", i) print("Dentro del ciclo.", i)
print("Fuera del ciclo.")

print("Fuera del ciclo.")


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
Módulo 3
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:"))
Módulo 3
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.
Módulo 3
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.
Módulo 3
No imprimas ninguna de las palabras ingresadas por el usuario. Utiliza el concepto de
ejecución condicional y la declaración break.
Módulo 3

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.


Módulo 3
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:
Módulo 3
G R G R Y
Entrada de muestra: abstemious

Salida esperada:
B S T M S
Entrada de muestra: IOUEA

Salida esperada:

# Indicar al usuario que ingrese una palabra


# y asignarlo a la variable userWord.

for letra in userWord:


# Completa el cuerpo del ciclo for.
Módulo 3

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.
Módulo 3
 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.


Módulo 3

Datos de prueba
Entrada de muestra: Gregory

Salida esperada:
GRGRY
Entrada de muestra: abstemious

Salida esperada:
BSTMS
Entrada de muestra: IOUEA

Salida esperada:

palabraSinVocal = ""
# Indicar al usuario que ingrese una palabra
# y asignarlo a la variable userWord
for letra in userWord:
# Completa el cuerpo del ciclo.
# Imprimir la palabra asignada a palabraSinVocal.
Módulo 3

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 print(i) else:
while i < 5: i += 1 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.

i = 1
Módulo 3
while i < 5:
print (i)
i += 1
else:
print("else:", i)

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 print(i) print("else:", i)
for i in range(2, 1): else:
¿Puedes adivinar la salida?

El cuerpo del ciclo no se ejecutará aquí en absoluto. Nota: hemos asignado la


variable i antes del ciclo.
Módulo 3
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)
Módulo 3

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.
Módulo 3
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
Módulo 3
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("Ingrese el número de bloques:"))


#
# Escribe tu código aquí.
#
print("La altura de la pirámide:", altura)
Módulo 3

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.
Módulo 3
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 70 106 160 40 10
23 35 53 80 20 5
Módulo 3
16 8 4 2 1 pasos = 17

Entrada de muestra: 16

Salida esperada:
8 4 2 1 pasos = 4
Entrada de muestra: 1023

Salida esperada:
3070 11663 7381 130 14 10
1535 34990 22144 65 7 5
4606 17495 11072 196 22 16
2303 52486 5536 98 11 8
6910 26243 2768 49 34 4
3455 78730 1384 148 17 2
10366 39365 692 74 52 1
5183 118096 346 37 26 pasos = 62
15550 59048 173 112 13
7775 29524 520 56 40
23326 14762 260 28 20
Módulo 3

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 contador = 5
while True: while contador > 2:
print("Atascado en un ciclo print(contador)
infinito") contador -= 1
# Ejemplo 2
 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 # Ejemplo 2
palabra = "Python" for i in range(1, 10):
for letter in palabra: if i % 2 == 0:
print(letter, fin = "*") print(i)
2. Puedes usar las sentencias break y continue para cambiar el flujo de un ciclo:
Módulo 3
 Utiliza break para salir de un ciclo, por ejemplo:
texto = "OpenEDG Python if letter == "P":
Institute" break
for letter in texto: print(letter, end= "")
 Utiliza continue para omitir la iteración actual, y continuar con la siguiente iteración, por
ejemplo:
text = "pyxpyxpyx" continue
for letter in text: print(letter, end= "")
if letter == "x":
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 print()
while n != 3: for i in range(0, 3):
print(n) print(i)
n += 1 else:
else: print(i, "else")
print(n, "else")
Módulo 3
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
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
Módulo 3
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 # line of code # line of code
while x < 11: # line of code

Revisar
Solución de muestra:
x = 1 if x % 2 != 0: x += 1
while x < 11: print(x)

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 "john.smith@pythoninstitute.org":
if ch == "@":
# línea de código
Módulo 3
# línea de código
Revisar
Solución de muestra:
for ch in "john.smith@pythoninstitute.org":
if ch == "@":
break
print(ch, end="")
Ejercicio 4
Crea un programa con un bucle for y una declaracióncontinue. 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": # línea de código
if digit == "0": # línea de código
# línea de código

Revisar
Solución de muestra:
for digit in "0165031806510": continue
if digit == "0": print(digit, end="")
print("x", end="")
Módulo 3
Ejercicio 5
¿Cuál es la salida del siguiente código?
n = 3 print(n + 1) else:
while n > 0: n -= 1 print(n)

Revisar
4 3 2 0

Ejercicio 6
¿Cuál es la salida del siguiente código?
n = range(4) print(num - 1) print(num)
for num in n: else:

Revisar
-1 0 1 2 3

Ejercicio 7
¿Cuál es la salida del siguiente código?
for i in range(0, 6, 3): print(i)

Revisar
0 3
Módulo 3

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:
Módulo 3
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 AyB


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
Módulo 3

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(var != 0)
print(not (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.
Módulo 3
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.
Módulo 3
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 ^)
Arg A Arg B Arg B & Arg B Arg A | Arg B Arg A ^ Arg B
0 0 0 0 0
0 1 0 1 1
1 0 0 1 1
1 1 1 1 0

Operaciones bitwise (~)


Arg ~Arg
0 1
1 0
Módulo 3
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
Módulo 3
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:
Módulo 3
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:
Módulo 3
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
Módulo 3
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: else:
# mi bit está listo # 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
Módulo 3
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:
Módulo 3
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.
Módulo 3
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 = += -= *= /= %= &= ^= |= >>= <<=


Módulo 3
var = 17
varRight = var >> 1
varLeft = var << 2
print(var, varLeft, varRight)

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.
Módulo 3
 | 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 b = x | y e = x >> 2
y = 1 c = ~ x f = x << 2
a = x & y d = x ^ 5 print(a, b, c, d, e, f)
Módulo 3
Revisar
0 5 -5 1 1 16

¿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()) var4 = int(input()) :
var2 = int(input()) var5 = int(input()) :
var3 = 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.
Módulo 3
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.
Módulo 3
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.

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?
Módulo 3
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.

numeros = [10, 5, 7, 2, 1]
print("Contenido de la lista:", numeros) # imprimiendo contenido de la lista
original.
Módulo 3

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).
Módulo 3
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

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.
Módulo 3
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.

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
Módulo 3
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 anterior
###
del numeros[1] # eliminando el segundo elemento de la lista
print("Longitud de la nueva lista:", len(numeros)) # imprimiendo nueva longitud de
la lista
print("\nNuevo contenido de la lista:", numeros) # imprimiendo el contenido de la
lista actual

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])
Módulo 3
El fragmento de ejemplo mostrará 2.

El último elemento accesible en nuestra lista es numeros[-4] (el primero). ¡No intentes ir más
lejos!

numeros = [111, 7, 2, 1]
print(numeros[-1])
print(numeros[-2])
Módulo 3

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:
Módulo 3
 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.
# Paso 2: escribe aquí una línea de código que elimine el último elemento de la
lista.
# Paso 3: escribe aquí una línea de código que imprima la longitud de la lista
existente.
print(listaSombrero)
Módulo 3

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)
Módulo 3
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.

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)
Módulo 3
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)
###
Módulo 3
numeros.append(4)
print(len(numeros))
print(numeros)
###
numeros.insert(0,222)
print(len(numeros))
print(numeros)
#

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 miLista.insert(0, i + 1)
for i in range(5): print(miLista)
¿Qué pasará ahora? Ejecuta el programa y comprueba si esta vez también tenías razón.
Módulo 3
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)

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.
Módulo 3
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 cómo funciona:
miLista = [10, 1, 8, 3, 5] suma += i
suma = 0 print(suma)
for i in miLista:
¿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.
Módulo 3
 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í.
miLista = [10, 1, 8, 3, 5]
suma = 0
for i in range(len(miLista)):
suma += miLista[i]
print(suma)

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 = variable1
variable2 = 2 variable1 = variable2
Módulo 3
Si haces algo como esto, perderás el valor previamente almacenadoenvariable2. Cambiar
el orden de las tareas no ayudará. Necesitas una tercera variable que sirva como
almacenamiento auxiliar.
Así es como puedes hacerlo:
variable1 = 1 auxiliar = variable1 variable2 = auxiliar
variable2 = 2 variable1 = variable2
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)
Módulo 3
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).
Módulo 3
 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.


Módulo 3

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.
Módulo 3
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 ciclofor 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
print("Paso 1:", beatles)
# paso 2
print("Paso 2:", beatles)
# paso 3
print("Paso 3:", beatles)
# etapa 4
Módulo 3
print("Paso 4:", beatles)
# paso 5
print("Paso 5:", beatles)
# probando la longitud de la lista
print("Los Fab", len(beatles))
Módulo 3

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]
Módulo 3
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] del lst[0] print(lst)
lst.insert(1, 6) lst.append(1)
Módulo 3
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] agregar += number
lst2 = [] lst2.append (agregar)
agregar = 0 print(lst2)
for number in lst:

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))
Módulo 3
Revisar
[2, 3] 3

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
Módulo 3
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.
Módulo 3
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.

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
Módulo 3
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.
Módulo 3

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 cómo 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 = []
Módulo 3
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)

Puntos clave
1. Puedes usar el método sort() para ordenar los elementos de una lista, por ejemplo:

lst = [5, 3, 1, 2, 4]
Módulo 3
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?
Módulo 3
a = 3 c = 2 lst.sort ()
b = 1 lst = [a, c, b] print(lst)

Revisar
[1, 2, 3]
Ejercicio 3
¿Cuál es la salida del siguiente fragmento de código?
a = "A" d = "" print(lst)
b = "B" lst = [a, b, c, d]
c = "C" lst.reverse ()

Revisar
['', 'C', 'B', 'A']

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.
Módulo 3
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 = lista1copia 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]
Módulo 3
lista2 = lista1
lista1[0] = 2
print(lista2)

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] lista1[0] = 2
lista2 = lista1[:] 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]
Módulo 3
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]

# Copiando toda la lista


lista1 = [1]
lista2 = lista1[:]
Módulo 3
lista1[0] = 2
print(lista2)
# Copiando parte de la lista
miLista = [10, 8, 6, 4, 2]
nuevaLista = miLista[1:3]
print(nuevaLista)

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)
Módulo 3
El resultado del fragmento es: [8, 6, 4].

Si elinicio 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]
Módulo 3
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].

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)
Módulo 3
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.
Módulo 3
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)
Módulo 3

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]
Módulo 3
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?

miLista = [17, 3, 11, 5, 1, 9, 7, 15, 13]


mayor = miLista[0]
for i in range(1, len(miLista)):
if miLista [i]> mayor:
mayor = miLista[i]
print(mayor)

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
Módulo 3
if Encontrado:
print("Elemento encontrado en el índice", i)
else:
print("ausente")
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:
Módulo 3
 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.


Módulo 3

LABORATORIO

Tiempo estimado
10-15 minutos

Nivel de dificultad
Fácil

Objetivos
Familiarizar al estudiante con:

 Indexación de listas.
 Utilizar operadoresin 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.
Módulo 3
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í
#
print("La lista solo con elementos únicos:")
print(miLista)
Módulo 3

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']
Módulo 3
4. Los parámetros inicio y finson 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]


Módulo 3
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"] l3 = l2 del l2[0]
l2 = l1 del l1[0] print(l3)

Revisar
['C']
Ejercicio 2
¿Cuál es la salida del siguiente fragmento de código?
l1 = ["A", "B", "C"] l3 = l2 del l2
l2 = l1 del l1[0] 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
Módulo 3
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"] l3 = l2[:] del l2[0]
l2 = l1[:] del l1[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"]
Módulo 3
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

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.
Módulo 3
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))
Módulo 3
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:
Módulo 3
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:
Módulo 3
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[7][0] = TORRE
tablero[0][7] = 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:


Módulo 3
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:
Módulo 3
 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, dpara 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)]
Módulo 3
#
# 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:
Módulo 3
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.")
Módulo 3

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
Módulo 3
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!

habitaciones =[[[False for r in range(20)] for f in range(15)] for t in


range(3)]

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:
Módulo 3
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: ':)'
Módulo 3
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: ':)'

¡Felicidades! Has completado el Módulo 3.


¡Bien hecho! Has llegado al final del Módulo 3 y has completado una meta importante en tu
educación de programación en Python. He aquí un breve resumen de los objetivos que has
cubierto y con lo que te has familiarizado en el Módulo 3:
Módulo 3
 Valores booleanos para comparar diferentes valores y controlar las rutas de ejecución
usando las instrucciones if e if-else.
 La utilización de bucles (while y for) y cómo controlar
su comportamiento utilizando las
instrucciones break y continue.
 La diferencia entre operaciones lógicas y bit a bit.
 El concepto de listas y procesamiento de listas,
incluyendo la iteración proporcionada por el bucle for,
y break.
 La idea de arreglos multidimensionales.

Ahora estás listo para tomar la prueba del módulo e intentar


el desafío final: la Prueba del Módulo 3, que te ayudará a
evaluar lo que has aprendido hasta ahora.
Módulo 4

Fundamentos de Programación en Python: Módulo 4

En este módulo, aprenderás:

 Cómo definir y utilizar funciones.


 Cómo pasar argumentos y las distintas formas de hacerlo.
 El alcance de los nombres.
 Tuplas y diccionarios.
 Procesamiento de datos.
Módulo 4

¿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.
Módulo 4
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.
Módulo 4
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.
Módulo 4

¿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
Módulo 4
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())

¿Cómo es que se crea dicha función?


Se necesita definirla. Aquí, la palabra define es significativa.
Módulo 4
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("Se comienza aquí.")
print("Ingresa un valor: ") print("Se termina aquí.")
Nota: no se esta utilizando la función, no se esta invocando en el código.
Módulo 4
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(): mensaje()
print("Ingresa un valor: ") print("Se termina aquí.")
print("Se comienza aquí.")
La salida ahora se ve diferente:
Se comienza aquí. Se termina aquí.
Ingresa un valor:
Prueba el código, modifícalo, experimenta con el.

print("Ingresa un valor: ")


a = int(input())
print("Ingresa un valor: ")
b = int(input())
print("Ingresa un valor: ")
Módulo 4
c = int(input())

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í.")
Módulo 4
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 más 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.
Módulo 4
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í.") mensaje()
def mensaje(): print("Se termina aquí.")
print("Ingresa un valor: ")
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(): mensaje()
print("Ingresa un valor: ") b = int(input())
mensaje() mensaje()
a = int(input()) 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.

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,
Módulo 4
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:

1. 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.
1. 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).
1. 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.
1. 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
Módulo 4
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)
Módulo 4
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.

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): ###
Módulo 4
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.
Módulo 4
Recuerda: especificar uno o más 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 número de argumentos como haya parámetros definidos.
El no hacerlo provocará un error.

Funciones con parámetros: continuación


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.
Módulo 4
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 número es una entidad completamente diferente de la variable


llamada número.
Esto significa que el código anterior producirá la siguiente salida:
Ingresa un número: 1 1234
def mensaje(numero):
print("Ingresa un número:", numero)
Módulo 4
mensaje()

Funciones con parámetros: continuación


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:
Módulo 4
Ingresa teléfono número 11 Ingresa número número número
Ingresa precio número 5
Ejecuta el código, modifícalo, agrega más parámetros y ve como esto afecta la salida.

def mensaje(que, numero):


print("Ingresa", que, "número", numero)
# invocar la función

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): miFuncion(1, 2, 3)
print(a, b, c)
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.
Módulo 4
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?
Módulo 4

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")
Módulo 4
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.
Módulo 4
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:


Módulo 4
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?

def suma(a, b, c):


print(a, "+", b, "+", c, "=", a + b + c)
# manda llamar la función suma aquí

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:
Módulo 4
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.
Módulo 4
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.

def presentar(primerNombre, segundoNombre="Smith"):


print("Hola, mi nombre es", primerNombre, segundoNombre)
# mandar llamar la función aquí (invocarla)
Módulo 4

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: ")
Módulo 4
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): resta(5, 2) # salida: 3
print(a - b) resta(2, 5) # salida: -3

Ejemplo 2
def resta(a, b): resta(a=5, b=2) # salida: 3
print(a - b) resta(b=2, a=5) # salida: 3

Ejemplo 3
def resta(a, b): resta(5, b=2) # salida: 3
print(a - b) 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): resta(5, b=2) # salida: 3
print(a - b) resta(a=5, 2) # Syntax Error
Módulo 4
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"):
Módulo 4
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)
Módulo 4

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.
Módulo 4
Consideremos la siguiente función:
def felizAñoNuevo(deseos = True): if not deseos:
print("Tres ...") return
print("Dos ...") print("¡Feliz año nuevo!")
print("Uno ...")
Cuando se invoca sin ningún argumento:
felizAñoNuevo()
La función produce un poco de ruido; la salida se verá así:
Tres ... Uno...
Dos... ¡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:


Módulo 4
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.
Módulo 4
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 ...
¿Está 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).
Módulo 4
 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í:
Módulo 4
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.
Módulo 4
def strangeFunction(n):
if(n % 2 == 0):
return True

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): for elem in lst: return sum
sum = 0 sum += elem
y se invoca así:
print(sumaDeLista([5, 4, 3]))
Regresará 12 como resultado, pero habrá problemas si la invocas de esta manera riesgosa:
Módulo 4
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.

def sumaDeLista(lst):
sum = 0
for elem in lst:
sum += elem
return sum

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.
Módulo 4
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.

def strangeListFunction(n):
strangeList = []
for i in range(0, n):
strangeList.insert(0, i)
return strangeList
print(strangeListFunction(5))
Módulo 4

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.


Módulo 4
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")
Módulo 4

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.
Módulo 4

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
#
Módulo 4
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")
Módulo 4

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.
Módulo 4

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))
Módulo 4

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.
Módulo 4

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):
Módulo 4
#
# coloca tu código aquí
#
for i in range(1, 20):
if isPrime(i + 1):
print(i + 1, end=" ")
print()
Módulo 4

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.
Módulo 4
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 # coloca tu código aqui
23.52145833333333 #
3.9007393587617467 def mpgtol100km(miles):
7.490910297239916 #
10.009131205673757 # coloca tu código aqui
def l100kmtompg(liters): #
Módulo 4
print(l100kmtompg(3.9)) print(mpgtol100km(60.3))
print(l100kmtompg(7.5)) print(mpgtol100km(31.4))
print(l100kmtompg(10.)) print(mpgtol100km(23.5))
Módulo 4

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():
Módulo 4
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.append(i)
myList = [] return myList
for i in range(n): print(createList(5))
Módulo 4
Ejercicio 1
¿Cuál es la salida del siguiente fragmento de código?
def hola(): print("¡Hola!")
return 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): return False
if type(data) == int: print(isInt(5))
return True print(isInt(5.0))
elif type(data) == float: print(isInt("5"))

Revisar
True False None

Ejercicio 3
¿Cuál es la salida del siguiente fragmento de código?
def evenNumLst(ran): for num in range(ran):
lst = [] if num % 2 == 0:
Módulo 4
lst.append(num) print(evenNumLst(11))
return lst

Revisar
[0, 2, 4, 6, 8, 10]
Ejercicio 4
¿Cuál es la salida del siguiente fragmento de código?
def listUpdater(lst): return updList
updList = [] l = [1, 2, 3, 4, 5]
for elem in lst: print(listUpdater(l))
elem **= 2 Revisar
updList.append(elem) [1, 4, 9, 16, 25]

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?
Módulo 4
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.
El resultado de la prueba es positivo, el código da como salida:
¿Conozco a la variable? 1 1
Módulo 4
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 más precisa y adecuada:


Módulo 4
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.

def miFuncion():
print("¿Conozco a la variable?", var)
var = 1
miFuncion()
print(var)

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.
Módulo 4
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.

Se ha agregado la palabra global a la función.

El código ahora da como salida:


¿Conozco a aquella variable? 2 2
Esto debe de ser suficiente evidencia para mostrar lo que la palabra reservada global puede
hacer.

def miFuncion():
global var
var = 2
print("¿Conozco a aquella variable?", var)
var = 1
Módulo 4
miFuncion()
print(var)

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.
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:
Módulo 4
def miFuncion(miLista1): miLista2 = [2, 3]
print(miLista1) miFuncion(miLista2)
miLista1 = [0, 1] 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): miLista2 = [2, 3]
print(miLista1) miFuncion(miLista2)
del miLista1[0] 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:
Módulo 4
 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.

def miFuncion(n):
print("Yo obtuve", n)
n += 1
print("Yo ahora tengo", n)
var = 1
miFuncion(var)
print(var)

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:
Módulo 4
var = 2 print(multByVar(7))
def multByVar(x): # salida: 14
return x * var

Ejemplo 2:
def mult(x): return x * var
var = 5 print(mult(7)) # salida: 35

Ejemplo 3:
def multip(x): var = 3
var = 7 print(multip(7)) # salida: 49
return x * var
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): print(sum(4)) # salida: 11
var = 7 print(var) # NameError
return x + var
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
Módulo 4
def retVar(): return var
global var print(retVar()) # salida: 5
var = 5 print(var) # salida: 5

Ejercicio 1
¿Qué ocurrirá cuando se intente ejecutar el siguiente código?
def message(): print("Hola, mundo!")
alt = 1 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 a = 2 fun()
def fun(): print(a) print(a)

Revisar
2 1

Ejercicio 3
¿Cuál es la salida del siguiente fragmento de código?
a = 1 def fun(): global a
Módulo 4
a = 2 fun() print(a)
print(a) a = 3

Revisar
2 3

Ejercicio 4
¿Cuál es la salida del siguiente fragmento de código?
a = 1 a = 2 fun()
def fun(): print(a) print(a)
global a a = 3

Revisar
2 2

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)
Módulo 4
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): print(imc(52.5, 1.65))
return peso / altura ** 2
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.

def imc(peso, altura):


return peso / altura ** 2
print(imc(52.5, 1.65))

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.
Módulo 4
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): print(lbakg(1))


return lb * 0.45359237
El resultado de la prueba es el siguiente:
0.45359237
Módulo 4
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
Módulo 4
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:
Módulo 4
27.565214082533313
Ejecuta el código y pruébalo.

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

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.


Módulo 4
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): return True
if a + b <= c or b + c <= a or \ print(esUnTriangulo(1, 1, 1))
c + a <= b: print(esUnTriangulo(1, 1, 3))
return False
¿Se puede compactar aun más?
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 ors con ands, 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.
Módulo 4
def esUnTriangulo(a, b, c): if c + a <= b:
if a + b <= c: return False
return False return True
if b + c <= a: print(esUnTriangulo (1, 1, 1))
return False print(esUnTriangulo (1, 1, 3))

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.

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
Módulo 4
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.

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):
Módulo 4
print("Felicidades, puede ser un triángulo.")
else:
print("Lo siento, no puede ser un triángulo.")

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):
Módulo 4
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.

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.")
Módulo 4
else:
print("Lo siento, no puede ser un triángulo.")

Algunas funciones simples: factoriales


La siguiente función a definir calcula factoriales. ¿Recuerdas cómo se calcula un factorial?
0! = 1 (¡Si!, es verdad.) 4! = 1 * 2 * 3 * 4
1! = 1 :
2! = 1 * 2 :
3! = 1 * 2 * 3 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): producto = 1
if n < 0: for i in range(2, n + 1):
return None producto *= i
if n < 2: return producto
return 1 for n in range(1, 6): # probando
Módulo 4
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 fib3 = 1 + 1 = 2 fib5 = 2 + 3 = 5 fib7 = 5 + 8 = 13
fib2 = 1 fib4 = 1 + 2 = 3 fib6 = 3 + 5 = 8
¿Que opinas acerca de implementarlo como una función?

Creemos nuestra propia función fib y probémosla, aquí esta:


Módulo 4
def fib(n): for i in range(3, n + 1):
if n < 1: sum = elem1 + elem2
return None elem1, elem2 = elem2, sum
if n < 3: return sum
return 1 for n in range(1, 10): # probando
elem1 = elem2 = 1 print(n, "->", fib(n))
sum = 0
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 3 -> 2 5 -> 5 7 -> 13 9 -> 34


2 -> 1 4 -> 3 6 -> 8 8 -> 21

Algunas funcione simples: recursividad


Existe algo más que se desea mostrar: es la recursividad.

Este término puede describir muchos conceptos distintos, pero uno de ellos, hace referencia
a la programación computacional.
Módulo 4
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 < 3:


if n < 1: return 1
return None 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
Módulo 4
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 < 2:
if n < 0: return 1
return None 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.
def fib(n): for i in range(3, n + 1):
if n < 1: sum = elem1 + elem2
return None elem1, elem2 = elem2, sum
if n < 3: return sum
return 1 for n in range(1, 10):
elem1 = elem2 = 1 print(n, "->", fib(n))
sum = 0
Módulo 4

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:
Módulo 4
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): else:
if a > 30: return a + fun(a + 3)
return 3 print(fun(25))

Revisar
56
Módulo 4

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)
Módulo 4
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)
Módulo 4
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.
Módulo 4
El programa debe de generar la siguiente salida, ejecútalo y comprueba:
1 (1, 10) 100
1000 1 1000
(10, 100, 1000) 10
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) del miTupla[0]
miTupla.append(10000) miTupla[1] = -10
Este es el mensaje que Python arrojará en la ventana de consola:
AttributeError: 'tuple' object has no attribute 'append'
miTupla = (1, 10, 100, 1000)
print(miTupla[0])
print(miTupla[-1])
print(miTupla[1:])
print(miTupla[:-2])
for elem in miTupla:
print(elem)
Módulo 4

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


La salida es la siguiente:
9 True
(1, 10, 100, 1000, 10000) True
(1, 10, 100, 1, 10, 100, 1, 10, 100)
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 t3 = (3, var)
t1 = (1, ) t1, t2, t3 = t2, t3, t1
t2 = (2, ) 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.
Módulo 4
Nota: el ejemplo presenta un importante hecho más: 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.

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)

¿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
Módulo 4
"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"}
Módulo 4
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}
{}
Módulo 4
¿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:
chat 5557654321
Módulo 4
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
Módulo 4
dict = {"gato" : "chat", "perro" : "chien", "caballo" : "cheval"}
numerosTelefono = {'jefe' : 5551234567, 'Suzy' : 22657854310}
diccionarioVacio = {}

¿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]
Módulo 4
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


dict = {"gato" : "chat", "perro" : "chien", "caballo" : "cheval"}
for key in dict.keys():
print(key, "->", dict[key])

¿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:
Módulo 4
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
Módulo 4
chat

dict = {"gato" : "chat", "perro" : "chien", "caballo" : "cheval"}


for spanish, french in dict.items():
print(spanish, "->", french)

¿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'}
Módulo 4

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)
Módulo 4

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'}
Módulo 4
En versiones anteriores de Python, por ejemplo, antes de la 3.6.7, el
método popitem() elimina un elemento al azar del diccionario.

dict = {"gato" : "perro", "dog" : "chien", "caballo" : "cheval"}


dict['gato'] = 'minou'
print(dict)

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 nombres y el promedio de cada alumno debe ser mostrada al final.

Observa el código en el editor, se muestra la solución.


Ahora se analizará línea por línea:
Módulo 4
 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
Módulo 4
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
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): "))
Módulo 4
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)

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]
Módulo 4
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]
Módulo 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 print(5 in t2) t4 = t1 + t2
t1 = (1, 2, 3) print(5 not in t2) t5 = t3 * 2
for elem in t1: # Ejemplo 3 print(t4)
print(elem) t3 = (1, 2, 3, 5) print(t5)
# Ejemplo 2 print(len(t3))
t2 = (1, 2, 3, 4) # Ejemplo 4
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:
Módulo 4
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:
Módulo 4
miDictionario = { clave3 : valor3,
clave1 : valor1, }
clave2 : valor2,
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 = {
Módulo 4
"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")
Módulo 4
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",
Módulo 4
"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 = { if "zamek" in polEspDict:


"zamek" : "castillo", print("SI")
"woda" : "agua", else:
"gleba" : "tierra" 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"
Módulo 4
}
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 = { "gleba" : "tierra"


"zamek" : "castillo", }
"woda" : "agua", 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.
Módulo 4
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+'}


Módulo 4
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"]
Módulo 4
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} miDict.clear()
copyMiDict = myDict.copy() print(copyMiDict)

Revisar
El programa mostrará {'A': 1, 'B': 2} en pantalla
Módulo 4
Ejercicio 8
¿Cuál es la salida del siguiente programa?
colores = { print(col, ":", rgb)
"blanco" : (255, 255, 255), Revisar
"gris" : (128, 128, 128), blanco : (255, 255, 255)
"rojo" : (255, 0, 0), gris : (128, 128, 128)
"verde" : (0, 128, 0) rojo : (255, 0, 0)
} verde : (0, 128, 0)
for col, rgb in colores.items():
Módulo 4

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.
Módulo 4
 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 número de cuadro elegido. El número
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
continúa, 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 | Ingresa tu movimiento: 1
| | | | +-------+-------+-------+
+-------+-------+-------+ | | | |
| | | | | O | 2 | 3 |
| 4 | X | 6 | | | | |
| | | | +-------+-------+-------+
+-------+-------+-------+ | | | |
| | | | | 4 | X | 6 |
| 7 | 8 | 9 | | | | |
Módulo 4
+-------+-------+-------+ | | | |
| | | | | 4 | X | 6 |
| 7 | 8 | 9 | | | | |
| | | | +-------+-------+-------+
+-------+-------+-------+ | | | |
+-------+-------+-------+ | 7 | O | 9 |
| | | | | | | |
| O | X | 3 | +-------+-------+-------+
| | | | +-------+-------+-------+
+-------+-------+-------+ | | | |
| | | | | O | X | 3 |
| 4 | X | 6 | | | | |
| | | | +-------+-------+-------+
+-------+-------+-------+ | | | |
| | | | | 4 | X | X |
| 7 | 8 | 9 | | | | |
| | | | +-------+-------+-------+
+-------+-------+-------+ | | | |
Ingresa tu movimiento: 8 | 7 | O | 9 |
+-------+-------+-------+ | | | |
| | | | +-------+-------+-------+
| O | X | 3 | Ingresa tu movimiento: 4
| | | | +-------+-------+-------+
+-------+-------+-------+ | | | |
Módulo 4
| O | X | 3 | | | | |
| | | | | 7 | O | 9 |
+-------+-------+-------+ | | | |
| | | | +-------+-------+-------+
| O | X | X | Ingresa tu movimiento: 7
| | | | +-------+-------+-------+
+-------+-------+-------+ | | | |
| | | | | O | X | X |
| 7 | O | 9 | | | | |
| | | | +-------+-------+-------+
+-------+-------+-------+ | | | |
+-------+-------+-------+ | O | X | X |
| | | | | | | |
| O | X | X | +-------+-------+-------+
| | | | | | | |
+-------+-------+-------+ | O | O | 9 |
| | | | | | | |
| O | X | X | +-------+-------+-------+
| | | | ¡Has Ganado!
+-------+-------+-------+
Módulo 4

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):
#
Módulo 4
# la función acepta un parámetro el cual contiene el estado actual del tablero
# y lo muestra en la consola
#
def EnterMove(board):
#
# la función acepta el estado actual del tablero y pregunta al usuario acerca
de su movimiento,
# verifica la entrada y actualiza el tablero acorde a la decisión del usuario
#
def MakeListOfFreeFields(board):
#
# la función examina el tablero y construye una lista de todos los cuadros
vacíos
# la lista esta compuesta por tuplas, cada tupla es un par de números que
indican la fila y columna
#
def VictoryFor(board, sign):
#
# la función analiza el estatus del tablero para verificar si
# el jugador que utiliza las 'O's o las 'X's ha ganado el juego
Módulo 4

¡Felicidades, has completado el Módulo 4!


¡Bien hecho! Has llegado al final del Módulo 4 y también has terminado una parte importante
de tu educación sobre programación en Python. A continuación se presenta un corto
resumen de los objetivos cubiertos en el módulo:

 La declaración y uso de funciones, su propósito, y sus usos.


 El concepto de cómo pasar argumentos de diferentes maneras y como colocarles valores
predeterminados junto con los mecanismos para que la función retorne o regrese
resultados.
 El alcance de los nombres de variables.
 Nuevos tipos de datos: tuplas y diccionarios, y su rol en el procesamiento de datos.

Ahora estas listo para tomar el quiz del módulo e intentar el desafío final: Examen del Módulo
4, el cual te ayudará a determinar que tanto has aprendido hasta ahora.
CISCO

Python Essentials Parte


Recursos de Estudio
Bienvenido a Fundamentos de Programación en Python - Parte 2

Módulo 5
Módulos, paquetes, cadenas, métodos de listas y excepciones.
Módulo 6

El Enfoque Orientado a Objetos: clases, métodos, objetos y sus características estándar:


manejo de excepciones y manejo de archivos.
Módulo 5

Fundamentos de Programación en Python: Módulo 5

En este módulo, aprenderás acerca de:

 Módulos de Python: su lógica, su función, cómo importarlos de diferentes maneras y


presentar el contenido de algunos módulos estándar proporcionados por Python.
 La forma en que los módulos se unen para formar paquetes.
 El concepto de una excepción y su implementación en Python, incluida la instrucción try-
except, con sus aplicaciones y la instrucción raise.
 Cadenas y sus métodos específicos, junto con sus similitudes y diferencias en comparación
con las listas.
Módulo 5

¿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.
Módulo 5
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:
Módulo 5
 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
Módulo 5
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
Módulo 5
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
Módulo 5
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.
Módulo 5

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.

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 print(math.sin(math.pi/2))


import math
Módulo 5

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.

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í.
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))
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
Módulo 5
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.
Módulo 5
¿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:

 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 return None # linea 07
def sin(x): print(sin(pi/2)) # linea 09
if 2 * x == pi: from math import sin, pi # linea 12
return 0.99999999 print(sin(pi/2)) # linea 14
else:
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).
Módulo 5
 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.
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))

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 (*).
Módulo 5
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.


Módulo 5

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))
Módulo 5
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.
Import math as m
print(m.sin(m.pi/2))

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.
Módulo 5
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.
Módulo 5
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:
Módulo 5

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.
Módulo 5
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?

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.
from math import pi, radians, degrees, sin, cos, tan, asin
ad = 90
ar = radians(ad)
ad = degrees(ar)
print(ad == 90.)
Módulo 5
print(ar == pi / 2.)
print(sin(ar) / cos(ar) == tan(ar))
print(asin(sin(ar)) == ar)

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)))
Módulo 5
print(pow(2, 2) == exp(2 * log(2)))
print(log(e, e) == exp(0))

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
Módulo 5
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))

¿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.
Módulo 5
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.
Módulo 5
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 for i in range(5):
seed(0) 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.258916750293
0.75795440294 0.511274721369
0.420571580831
¿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.
Módulo 5
from random import random
for i in range(5):
print(random())

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).
Módulo 5
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))

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.
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".
Módulo 5
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 print(sample(lst, 5))
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] print(sample(lst, 10))
print(choice(lst))
Nuevamente, la salida del programa no es predecible. Nuestros resultados se ven así:
4 [10, 8, 5, 1, 6, 4, 3, 9, 7, 2]
[3, 1, 8, 9, 10]
from random import randint
for i in range(10):
print(randint(1, 10), end=',')
Módulo 5

¿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.
Módulo 5
 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,
Módulo 5
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:
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
Módulo 5
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))

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:
Módulo 5
Intel x86 + Windows ® Vista (32 bit):
x86
Intel x86 + Gentoo Linux (64 bit):
x86_64
Raspberry PI2 + Raspbian Linux (32 bit):
armv7l
from platform import machine
print(machine())

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:
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
Módulo 5
from platform import processor
print(processor())

Una función llamada system() devuelve el nombre genérico del sistema operativo en una
cadena.
Nuestras plataformas de ejemplo se presentaron así:
Intel x86 + Windows ® Vista (32 bit):
Windows
Intel x86 + Gentoo Linux (64 bit):
Linux
Raspberry PI2 + Raspbian Linux (32 bit):
Linux
from platform import system
print(system())

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:


Intel x86 + Windows ® Vista (32 bit):
6.0.6002
Módulo 5
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)
from platform import version
print(version())

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:
 la parte mayor de la versión de Python.
 la parte menor,
 el número de nivel del patch.

Nuestro programa ejemplo produjo el siguiente resultado:


CPython 3 6 4
Es muy probable que tu versión de Python sea diferente.
from platform import python_implementation, python_version_tuple
Módulo 5
print(python_implementation())
for atr in python_version_tuple():
print(atr)

Í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.
Módulo 5
Módulo 5
En la siguiente sección veremos cómo escribir módulos propios.

¿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.
Módulo 5

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?
Módulo 5
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.
Módulo 5

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:
Módulo 5
 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:
Módulo 5
 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:
Módulo 5
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.
Módulo 5
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
Módulo 5

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á).
Módulo 5
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í:
Módulo 5
Ver código en Sandbox

# main.py
from module import suml, prodl
zeroes = [0 for i in range(5)]
ones = [1 for i in range(5)]
print(suml(zeroes))
print(prodl(ones))

Nota:

 Se ha duplicado la \ dentro del nombre de la carpeta, ¿sabes por qué?


Revisar
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().
Módulo 5

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:
Módulo 5
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.
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:
Módulo 5
 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 primera 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.
Módulo 5
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.


Módulo 5

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.
Módulo 5

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.
Módulo 5

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

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:
Módulo 5
Ingresa x: -1 y = math.sqrt(x)
Traceback (most recent call last): ValueError: math domain error
File "sqrt.py", line 4, in
¿Puedes protegerte de tales sorpresas? Por supuesto que si. Además, tienes que hacerlo para
ser considerado un buen programador.
import math
x = float(input("Ingresa x: "))
y = math.sqrt(x)
print("La raíz cuadrada de", x, "es igual a", y)

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.
Módulo 5
 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.
Verás el siguiente mensaje en respuesta:
Traceback (most recent call last): valor /= 0
File "div.py", line 2, in ZeroDivisionError: division by zero
Módulo 5
Este error de excepción se llama ZeroDivisionError.
valor = 1 valor /= 0

Excepciones: continuación
Observa el código en el editor. ¿Qué pasará cuando lo ejecutes?
Verás el siguiente mensaje en respuesta:
Traceback (most recent call last): x = lista[0]
File "lst.py", line 2, in IndexError: list index out of range
Este es el IndexError (error de índice).
lista = [] x = lista[0]

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.
Módulo 5
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.

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.
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.")

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.
Módulo 5
 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.


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.
primerNumero = int(input("Ingresa el primer numero: "))
segundoNumero = int(input("Ingresa el segundo numero: "))
Módulo 5
if segundoNumero != 0:
print(primerNumero / segundoNumero)
else:
print("Esta operacion no puede ser realizada.")
print("FIN.")

Excepciones: continuación
Observa el código en el editor. Te ayudará a comprender este mecanismo.
Esta es la salida que produce:
1 3
Oh cielos, algo salio mal...
Nota: la instrucción print("2") se perdió en el proceso.

try:
print("1")
x = 1 / 0
print("2")
except:
Módulo 5
print("Oh cielos, algo salio mal...")
print("3")

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.

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:
Módulo 5
 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.


try:
x = int(input("Ingresa un numero: "))
y = 1 / x
except:
print("Oh cielos, algo salio mal...")
print("FIN.")

Mira el código en el editor. Nuestra solucion esta ahí.


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.
Módulo 5
 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.
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.")

No olvides que:
 Los bloques except son analizados en el mismo orden en que aparecen en el código.
Módulo 5
 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.

¿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.
try:
x = int(input("Ingresa un numero: "))
Módulo 5
y = 1 / x
print(y)
except ValueError:
print("Debes ingresar un valor entero.")
except:
print("Oh cielos, algo salio mal...")
print("FIN.")

Echemos a perder el código una vez más.


Observa el programa en el editor. Esta vez, hemos eliminado el bloque sin nombre.

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.
Módulo 5
try:
x = int(input("Ingresa un numero: "))
y = 1 / x
print(y)
except ValueError:
print("Debes ingresar un valor entero.")
print("FIN.")

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
Módulo 5
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
Módulo 5
Te mostraremos cómo funciona esta generalización. Comencemos con un código realmente
simple.
Observa el código en el editor. Es un ejemplo simple para comenzar. Ejecutalo.
La salida que esperamos ver se ve así:
Uuuppsss... y = 1 / 0
FIN. except ArithmeticError:
Ahora observa el código a print("Uuuppsss...")
continuación: print("FIN.")
try:
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 primera coincidencia.
Módulo 5
 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.
try:
y = 1 / 0
except ZeroDivisionError:
print("Uuuppsss...")
print("FIN.")

Mira el código en el editor. ¿Qué pasará aquí?

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: except ZeroDivisionError:


y = 1 / 0 print("¡División entre Cero!")
except ArithmeticError: print("FIN.")
print("¡Problema aritmético!")
El cambio es radical: la salida del código es ahora:
Módulo 5
¡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.

try:
y = 1 / 0
except ZeroDivisionError:
print("¡División entre Cero!")
except ArithmeticError:
print("¡Problema aritmético!")
print("FIN.")
Si deseas manejar dos o más excepciones de la misma manera, puedes usar la siguiente
sintaxis:
try: except (exc1, exc2):
: :
Módulo 5
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.

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:
Módulo 5
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.
def badFun(n):
try:
return 1 / n
except ArithmeticError:
print("¡Problema aritmético!")
return None
Módulo 5
badFun(0)
print("FIN.")

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.


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.
def badFun(n):
raise ZeroDivisionError
try:
Módulo 5
badFun(0)
except ArithmeticError:
print("¿Que pasó? ¿Un error?")
print("FIN.")

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.

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.
Módulo 5
def badFun(n):
try:
return n / 0
except:
print("¡Lo hice otra vez!")
raise
try:
badFun(0)
except ArithmeticError:
print("¡Ya veo!")
print("FIN.")

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.
Módulo 5
 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.

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): assert x >= 0.0
File ".main.py", line 4, in AssertionError
import math
x = float(input("Ingresa un numero: "))
assert x >= 0.0
Módulo 5
x = math.sqrt(x)
print(x)

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:
Módulo 5
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)))
Módulo 5

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
Módulo 5
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:
Módulo 5
# este código no puede ser terminado print(seconds)
# presionando Ctrl-C seconds += 1
from time import sleep sleep(1)
seconds = 0 except KeyboardInterrupt:
while True: print("¡No hagas eso!")
try: 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:
Módulo 5
# 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:
Módulo 5
# 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á?
Módulo 5
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:
Módulo 5
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.
Módulo 5

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.
Módulo 5
 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):
#
Módulo 5
# tu codigo aqui
#
v = readint("Ingresa un numero de -10 a 10: ", -10, 10)
print("El numero es:", v)
Módulo 5

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
Módulo 5
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.
Módulo 5
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.
Módulo 5
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 número 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.
Módulo 5
¿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.
Módulo 5
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.
Módulo 5

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.


Módulo 5

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.

Cualquier cadena puede estar vacía. Si es el caso, su longitud es 0 como en el Ejemplo 2.

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.

Ejecuta los tres ejemplos de código y verificalo.


# Ejemplo 1
palabra = 'por'
print(len(palabra))
Módulo 5
# Ejemplo 2
vacio = ''
print(len(vacio))
# Ejemplo 3
yo_soy = 'I\'m'
print(len(yo_soy))

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.

Como puedes ver, la cadena comienza con tres apóstrofes, no uno. El mismo apóstrofe
triplicado se usa para terminar la cadena.
Módulo 5
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.
multiLinea = '''Linea #1
Linea #2'''
print(len(multiLinea))
Módulo 5

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:

 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:


Módulo 5
ab ba aaaaa bbbb
Nota: Los atajos de los operadores anteriores también son aplicables para las cadenas
(+= y *=).

str1 = 'a'
str2 = 'b'
print(str1 + str2)
print(str2 + str1)
print(5 * 'a')
print('b' * 4)

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:
97 32
Módulo 5
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.
# Demostrando la función ord ()
ch1 = 'a'
ch2 = ' ' # espacio
print(ord(ch1))
print(ord(ch2))

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:


a α
Nota:
 chr(ord(x)) == x

 ord(chr(x)) == x
Módulo 5
De nuevo, realiza tus propios experimentos.
# Demostrando la función chr()
print(chr(97))
print(chr(945))

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.
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:
Módulo 5
# Iterando a través de una cadena print(ch, end=' ')
exampleString = 'silly walks' print()
for ch in exampleString:
La salida es la misma que el ejemplo anterior, revisalo.
# Indexando cadenas
exampleString = 'silly walks'
for ix in range(len(exampleString)):
print(exampleString[ix], end=' ')
print()

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.

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 abd e beg
efg e adf
Módulo 5
Ahora haz tus propios experimentos.
# 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])

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.


Módulo 5
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
alpfabeto = "abcdefghijklmnopqrstuvwxyz"
print("f" in alpfabeto)
print("F" in alpfabeto)
print("1" in alpfabeto)
print("ghi" in alpfabeto)
print("Xyz" in alpfabeto)
Módulo 5

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:
Módulo 5
alfabeto = "abcdefghijklmnopqrstuvwxyz"
alfabeto.insert(0, "A")
alfabeto = "abcdefghijklmnopqrstuvwxyz"
# pon el código de prueba aquí

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:
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.
alfabeto = "bcdefghijklmnopqrstuvwxy"
alfabeto = "a" + alfabeto
alfabeto = alfabeto + "z"
print(alfabeto)
Módulo 5

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:


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

Como puedes ver, presentan más que solo cadenas. El resultado esperado se ve de la
siguiente manera:
[ ] 0
Nota: hemos utilizado corchetes para evitar que el espacio se pase por alto en tu pantalla.
# Demonstrando min() - Ejemplo 1
Módulo 5
print(min("aAbByYzZ"))
# Demonstrando min() - Examplos 2 y 3
t = 'Los Caballeros Que Dicen "¡Ni!"'
print('[' + min(t) + ']')
t = [0, 1, 2]
print(min(t))

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:


Z
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.
La salida esperada es:
[¡] 2
Realiza tus propios experimentos.
Módulo 5
# Demostrando max() - Ejemplo 1
print(max("aAbByYzZ"))
# Demonstrando max() - Examplos 2 y 3
t = 'Los Caballeros Que Dicen "¡Ni!"'
print('[' + max(t) + ']')
t = [0, 1, 2]
print(max(t))

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:


2 7 1
Módulo 5
# 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.
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?
Módulo 5
Es:
2 0
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.
# Demostrando la función list()
print(list("abcabc"))
# Demostrando el método count()
print("abcabc".count("b"))
print('abcabc'.count("d"))

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.
Módulo 5
 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.
Esto es lo que imprime:
Abcd
Prueba algunos ejemplos más avanzados y prueba su salida:
print("Alpha".capitalize()) print('123'.capitalize())
print('ALPHA'.capitalize()) print("αβγδ".capitalize())
print(' Alpha'.capitalize())
# Demostración del método capitalize()
Módulo 5
print('aBcD'.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.
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(6) + ']')


print('[' + 'Beta'.center(4) + ']')
Ejecuta el código anterior y verifica qué salidas produce.
Módulo 5
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.
# Demostración del método center()
print('[' + 'alfa'.center(10) + ']')

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:
Si
Ahora deberías poder predecir la salida del fragmento de código a continuación:
t = "zeta" print(t.endswith("a"))
Módulo 5
print(t.endswith("A")) print(t.endswith("eta"))
print(t.endswith("et"))
Ejecuta el código para verificar tus predicciones.
# Demostración del método endswith()
if "epsilon".endswith("on"):
print("si")
else:
print("no")

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.


El ejemplo imprime:
1 -1
Módulo 5
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('et')) print(t.find('ha'))
print(t.find('eta')) print(t.find('te'))
¿Puedes predecir la salida? Ejecútalo y verifica tus predicciones.

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
Módulo 5
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 198 238
80 221
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:
Módulo 5
1 -1
a no se puede encontrar dentro de los límites de búsqueda dados en el segundo print().

# Demostración del método find()


print("Eta".find("ta"))
print("Eta".find("mma"))

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.

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 False
True False False
Hay tres más ejemplos aquí:

t = 'Six lambdas' t = 'ΑβΓδ' t = '20E1'


print(t.isalnum()) print(t.isalnum()) print(t.isalnum())
Módulo 5
Ejecútalos y verifica su salida.
Nota: la causa del primer resultado es un espacio, no es ni un dígito ni una letra.
# Demostración del método the isalnum()
print('lambda30'.isalnum())
print('lambda'.isalnum())
print('30'.isalnum())
print('@'.isalnum())
print('lambda_30'.isalnum())
print(''.isalnum())

El método isalpha()
El método isalpha() es más especializado, se interesa en letras solamente.
Observa el Ejemplo 1, su salida es:
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:


True False
Módulo 5
Realiza más experimentos.
# Ejemplo 1: Demostración del método isapha()
print("Moooo".isalpha())
print('Mu40'.isalpha())
# Ejemplo 2: Demostración del método isdigit()
print('2018'.isdigit())
print("Año2019".isdigit())

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:


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:


True True False
Módulo 5

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:
False False True
# Ejemplo 1: Demostración del método islower()
print("Moooo".islower())
print('moooo'.islower())
# Ejemplo 2: Demostración del método isspace()
print(' \n '.isspace())
print(" ".isspace())
print("mooo mooo mooo".isspace())
# Ejemplo 3: Demostración del método isupper()
print("Moooo".isupper())
print('moooo'.isupper())
print('MOOOO'.isupper())

El método join()
El método join() es algo complicado, así que déjanos guiarte paso a paso:
Módulo 5
 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:

 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
# Demostración del método join()
print(",".join(["omicron", "pi", "rho"]))

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.
Módulo 5
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:


sigma=60
Como ya sabes, realiza tus propios experimentos.
# Demostración del método lower()
print("SiGmA=60".lower())

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.


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:
Módulo 5
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"))
¿Sorprendido? Nuevamente, experimenta con tus propios ejemplos.
# Demostración del método the lstrip()
print("[" + " tau ".lstrip() + "]")

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.


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:
Módulo 5
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.
# 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 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.
Módulo 5
# 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))

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

El método split()
El método split() divide la cadena y crea una lista de todas las subcadenas detectadas.
Módulo 5
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:
['phi', 'chi', 'psi']
Nota: la operación inversa se puede realizar por el método join().

# Demostración del método split()


print("phi chi\npsi".split())

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:

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.
Módulo 5
Observa el segundo ejemplo en el editor. Este es el resultado que devuelve:
[aleph]
Ahora realiza tus propios experimentos con los dos métodos.
# Demostración del método startswith()
print("omega".startswith("meg"))
print("omega".startswith("om"))
print()
# Demostración del método strip()
print("[" + " aleph ".strip() + "]")

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?
yO SÉ QUE NO SÉ NADA.
Módulo 5

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:


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:
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.
# Demostración del método swapcase()
print("Yo sé que no sé nada.".swapcase())
print()
Módulo 5
# Demostración del método title()
print("Yo sé que no sé nada. Parte 1.".title())
print()
# Demostración del método upper()
print("Yo sé que no sé nada. Parte 2.".upper())
Módulo 5

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.
Módulo 5
 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.

Salida esperada
['Ser', 'o', 'no', 'ser', 'esa', 'es,', 'la', 'pregunta']
['Ser', 'o', 'no', 'ser,esa', 'es', 'la', 'pregunta']
[]
['abc']
[]
def misplit(strng):
#
# coloca tu código aquí
#
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(""))
Módulo 5

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:


Módulo 5
'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'


# Prueba los ejemplos aquí

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:
Módulo 5
'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
Ejecuta todos los ejemplos y realiza más experimentos.
# Prueba los ejemplos aquí
Módulo 5

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:
['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().

El resultado no ha cambiado:
Módulo 5
['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.
# Demostración de la función sorted()
firstGreek = ['omega', 'alfa', 'pi', 'gama']
firstGreek2 = sorted(firstGreek)
print(firstGreek)
print(firstGreek2)
print()
# Demostración del método sort()
secondGreek = ['omega', 'alfa', 'pi', 'gama']
print(secondGreek)
secondGreek.sort()
print(secondGreek)

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.
Módulo 5
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 si = str(itg) print(si + ' ' + sf)
flt = 1.3 sf = str(flt)
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' itg = int(si) print(itg + flt)
sf = '1.3' flt = float(sf)
Esto es lo que verás en la consola:
14.3
En la siguiente sección, te mostraremos algunos programas simples que procesan cadenas.
# Prueba el código aquí
Módulo 5

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.
Módulo 5
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:
# ### ###
# # #
# ### ###
# # #
# ### ###
Módulo 5
Entrada de muestra:
9081726354
Salida de muestra:
### ### ### # ### ### ### ### ### # #
# # # # # # # # # # # # # #
### # # ### # # ### ### ### ### ###
# # # # # # # # # # # # #
### ### ### # # ### ### ### ### #
Módulo 5

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.
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...
Módulo 5
 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
Haz tus propias pruebas.
# Cifrado César
text = input("Ingresa tu mensaje: ")
cifrado = ''
for char in text:
if not char.isalpha():
continue
Módulo 5
char = char.upper()
code = ord(char) + 1
if code > ord('Z'):
code = ord('A')
cifrado += chr(code)
print(cifrado)

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.
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.
Módulo 5
 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?
#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.")
Módulo 5

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).
Módulo 5
 (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:

 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.
Módulo 5
 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.
# 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:
Módulo 5
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.")
Módulo 5

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.
Módulo 5
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
Módulo 5

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.


Módulo 5
 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
Módulo 5

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.
Módulo 5
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
Módulo 5

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
Módulo 5
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:
6
Entrada de Ejemplo:
20000101
Salida del Ejemplo:
4
Módulo 5

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:
Módulo 5
 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
Módulo 5

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


Módulo 5
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 387459216 763524189
431865927 612387495 928671354
876192543 549216738 154938672
Salida de la Muestra:
Yes
Entrada de Muestra:
195743862 387459216 763524189
431865927 612387495 928671354
876192543 549216738 254938
Salida de la Muestra
No
Módulo 6

Fundamentos de Programación en Python: Módulo 6

En este módulo, aprenderás sobre:

 Los fundamentos y enfoque de programación orientada a objetos.


 Clases, métodos y objetos.
 Manejo de excepciones.
 Manejo de archivos.
Módulo 6

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.
Módulo 6
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.
Módulo 6
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
Módulo 6
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.
Módulo 6
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.


Módulo 6
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.
Módulo 6
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.
Módulo 6
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.
Módulo 6
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.
Módulo 6
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.
Módulo 6

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.
Módulo 6

¿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.
Módulo 6

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.
Módulo 6
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)
Módulo 6
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.
Módulo 6
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:
Módulo 6
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: print("¡Hola!")
def __init__(self): objetoPila = Pila()
Módulo 6
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.


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.
class Pila: # define la clase Pila
def __init__(self): # define la función del constructor
print("¡Hola!")
objetoPila = Pila() # instanciando el objeto

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.
Módulo 6
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: objetoPila = Pila()
def __init__(self): print(len(objetoPila.listaPila))
self.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:


0
Módulo 6
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.
class Pila:
def __init__(self):
self.listaPila = []
objetoPila = Pila()
print(len(objetoPila.listaPila))

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: objetoPila = Pila()
def __init__(self): print(len(objetoPila.__listaPila))
self.__listaPila = []
El cambio invalida el programa..
¿Por qué?
Módulo 6
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).
Módulo 6
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: return val


def __init__(self): objetoPila = Pila()
self.__listaPila = [] objetoPila.push(3)
def push(self, val): objetoPila.push(2)
self.__listaPila.append(val) objetoPila.push(1)
def pop(self): print(objetoPila.pop())
val = self.__listaPila[-1] print(objetoPila.pop())
del self.__listaPila[-1] 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.
Módulo 6
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.
class Pila:
def __init__(self):
self.__listaPila = []
Módulo 6
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())

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.
Módulo 6
Analiza el código:
class Pila: return val
def __init__(self): objetoPila1 = Pila()
self.__listaPila = [] objetoPila2 = Pila()
def push(self, val): objetoPila1.push(3)
self.__listaPila.append(val) objetoPila2.push(objetoPila1.pop()
)
def pop(self):
print(objetoPila2.pop())
val = self.__listaPila[-1]
del self.__listaPila[-1]
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.
class Pila:
def __init__(self):
self.__listaPila = []
def push(self, val):
self.__listaPila.append(val)
def pop(self):
Módulo 6
val = self.__listaPila[-1]
del self.__listaPila[-1]
return val
objetoPila1 = Pila()
objetoPila2 = Pila()
objetoPila1.push(3)
objetoPila2.push(objetoPila1.pop())
print(objetoPila2.pop())

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: return val
def __init__(self): pequeñaPila = Pila()
self.__listaPila = [] otraPila = Pila()
def push(self, val): graciosaPila = Pila()
self.__listaPila.append(val) pequeñaPila.push(1)
def pop(self): otraPila.push(pequeñaPila.pop() + 1)
val = self.__listaPila[-1] graciosaPila.push(otraPila.pop() - 2)
del self.__listaPila[-1] print(graciosaPila.pop())
Módulo 6
Entonces, ¿cuál es el resultado? Ejecuta el programa y comprueba si tenías razón.
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
# ingresa código aquí

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
Módulo 6
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): Pila.__init__(self)
def __init__(self): self.__sum = 0
Módulo 6
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.
Módulo 6
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í?
Módulo 6
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).
Módulo 6
Se dice que el método push ha sido anulado - el mismo nombre que en la superclase ahora
representa una funcionalidad diferente.

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
# ingresa código aquí

El enfoque orientado a objetos: una pila desde cero (continuación)


Esta es la nueva función pop:

def pop(self): self.__sum -= val


val = Pila.pop(self) return val
Módulo 6
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):
Módulo 6
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)

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.
Módulo 6
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: objetoEjemplo2.setSegunda(3)
def __init__(self, val = 1): objetoEjemplo3 = ClaseEjemplo(4)
self.primera = val objetoEjemplo3.tercera = 5
def setSegunda(self, val): print(objetoEjemplo1.__dict__)
self.segunda = val print(objetoEjemplo2.__dict__)
objetoEjemplo1 = ClaseEjemplo() print(objetoEjemplo3.__dict__)
objetoEjemplo2 = ClaseEjemplo(2)
Módulo 6
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:
 objetoEjemplo1 solo tiene una propiedad llamada primera.
 objetoEjemplo2 tiene dos propiedades: primera y segunda.
 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.
Módulo 6
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.

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}
Módulo 6
{'_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.
class ClaseEjemplo:
def __init__(self, val = 1):
self.__primera = val
def setSegunda(self, val):
self.__segunda = val
Módulo 6
objetoEjemplo1 = ClaseEjemplo()
objetoEjemplo2 = ClaseEjemplo(2)
objetoEjemplo2.setSegunda(3)
objetoEjemplo3 = ClaseEjemplo(4)
objetoEjemplo3.__tercera = 5
print(objetoEjemplo1.__dict__)
print(objetoEjemplo2.__dict__)
print(objetoEjemplo3.__dict__)

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
Módulo 6
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:
Módulo 6
 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)
Módulo 6
print(objetoEjemplo1.__dict__, objetoEjemplo1._ClaseEjemplo__contador)
print(objetoEjemplo2.__dict__, objetoEjemplo2._ClaseEjemplo__contador)
print(objetoEjemplo3.__dict__, objetoEjemplo3._ClaseEjemplo__contador)

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


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:
 El cambiar la asignación a self.varia = val crearía una variable de instancia con el
mismo nombre que la clase.
 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).
Módulo 6
 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.

class ClaseEjemplo:
varia = 1
def __init__(self, val):
ClaseEjemplo.varia = val
print(ClaseEjemplo.__dict__)
objetoEjemplo = ClaseEjemplo(2)
print(ClaseEjemplo.__dict__)
print(objetoEjemplo.__dict__)
Módulo 6

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.

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:


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

class ClaseEjemplo:
def __init__(self, val):
if val % 2 != 0:
self.a = 1
Módulo 6
else:
self.b = 1
objetoEjemplo = ClaseEjemplo(1)
print(objetoEjemplo.a)
print(objetoEjemplo.b)

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.

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).
Módulo 6
La función retorna True o False.

Así es como puedes utilizarla:


class ClaseEjemplo: self.b = 1
def __init__(self, val): objetoEjemplo = ClaseEjemplo(1)
if val % 2 != 0: print(objetoEjemplo.a)
self.a = 1 if hasattr(objetoEjemplo, 'b'):
else: print(objetoEjemplo.b)
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
Módulo 6

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.
Un ejemplo más: analiza el código a continuación e intenta predecir su salida:
class ClaseEjemplo: print(hasattr(objetoEjemplo, 'b'))
a = 1 print(hasattr(objetoEjemplo, 'a'))
def __init__(self): print(hasattr(ClaseEjemplo, 'b'))
self.b = 2 print(hasattr(ClaseEjemplo, 'a'))
objetoEjemplo = ClaseEjemplo()
¿Tuviste éxito? Ejecuta el código para verificar tus predicciones.

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.
class ClaseEjemplo:
attr = 1
print(hasattr(ClaseEjemplo, 'attr'))
print(hasattr(ClaseEjemplo, 'prop'))
Módulo 6

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


Módulo 6
 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: obj.metodo(1)
def metodo(self, par): obj.metodo(2)
print("método:", par) obj.metodo(3)
obj = conClase()
El código da como salida:

método: 1 método: 2 método: 3


class conClase:
def metodo(self):
print("método")
obj = conClase()
obj.metodo()

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.
Módulo 6
El ejemplo muestra ambas formas de utilizar el parámetro self:

class conClase: obj = conClase()


varia = 2 obj.var = 3
def metodo(self): obj.metodo()
print(self.varia, self.var)
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(): print("método")
def otro(self): self.otro()
print("otro") obj = conClase()
def metodo(self): 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.
Módulo 6
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.
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).
class conClase:
def __init__(self, valor):
Módulo 6
self.var = valor
obj1 = conClase("objeto")
print(obj1.var)

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.
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: obj.visible()
def visible(self): try:
print("visible") obj.__oculto()
def __oculto(self): except:
print("oculto") print("fallido")
obj = conClase() obj._conClase__oculto()
Módulo 6
El código da como salida:
visible fallido oculto
Ejecuta el programa y pruébalo.
class conClase:
def __init__(self, valor = None):
self.var = valor
obj1 = conClase("objeto")
obj2 = conClase()
print(obj1.var)
print(obj2.var)

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.
Ejecútalo para ver qué produce. Verifica el resultado.
Módulo 6
Encuentra todos los métodos y atributos definidos. Localiza el contexto en el que existen:
dentro del objeto o dentro de la clase.
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__)

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.


Módulo 6
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.
La salida del código es:
conClase conClase
Nota: algo como esto print(obj.__name__) causará un error.

class conClase:
pass
print(conClase.__name__)
obj = conClase()
print(type(obj).__name__)

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.


Módulo 6
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.
class conClase:
pass
print(conClase.__module__)
obj = conClase()
print(obj.__module__)

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.
Módulo 6
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. 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.
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)
Módulo 6

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.
Módulo 6
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!
Módulo 6
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__)
Módulo 6

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.
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.
class Estrella:
def __init__(self, nombre, galaxia):
self.nombre = nombre
self.galaxia = galaxia
sol = Estrella("Sol", "Vía Láctea")
print(sol)
Módulo 6

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.

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.
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)
Módulo 6

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:
Módulo 6
class Vehiculo: pass
pass class
VehiculoOruga(VehiculoTerrestre):
class VehiculoTerrestre(Vehiculo):
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.
Módulo 6
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.

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.
class Vehiculo:
Módulo 6
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()

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.
Módulo 6
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.

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:
Módulo 6

↓ es una instancia de → Vehiculo miVehiculoTerrestre VehiculoOruga


miVehiculo True False False
miVehiculoTerrestre True True False
VehiculoOruga True True True

¿La tabla confirma nuestras expectativas?


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()
Módulo 6

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:

 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.
Módulo 6
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.

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)
Módulo 6

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:

 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 llamadaSub. 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.

class Super:
def __init__(self, nombre):
Módulo 6
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)

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.
Módulo 6
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)

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.
Módulo 6
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:
2 1
# Probando propiedades: variables de clase
class Super:
supVar = 1
class Sub(Super):
subVar = 2
obj = Sub()
print(obj.subVar)
print(obj.supVar)

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.
Módulo 6
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).
# 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)
Módulo 6

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.

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.
Módulo 6
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

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í:
Módulo 6
class SuperA: return 21
varA = 10 class Sub(SuperA, SuperB):
def funA(self): pass
return 11 obj = Sub()
class SuperB: print(obj.varA, obj.funA())
varB = 20 print(obj.varB, obj.funB())
def funB(self):
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?
class SuperA:
varA = 10
def funA(self):
return 11
class SuperB:
varB = 20
def funB(self):
Módulo 6
return 21
class Sub(SuperA, SuperB):
pass
obj = Sub()
print(obj.varA, obj.funA())
print(obj.varB, obj.funB())

Cómo Python encuentra propiedades y métodos: continuación


Analicemos el ejemplo en el editor.

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.
Módulo 6
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.
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())

Cómo Python encuentra propiedades y métodos: continuación


Echemos un vistazo al ejemplo en el editor.

La clase Sub hereda todos los bienes de dos superclases, Izquierda y Derecha (estos
nombres están destinados a ser significativos).
Módulo 6
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?
Módulo 6
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())

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),
Módulo 6
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:

 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
Módulo 6
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.
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()
Módulo 6

Cómo construir una jerarquía de clases: continuación


Mira el ejemplo en el editor.

¿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.
Módulo 6
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.
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)
Módulo 6

Cómo construir una jerarquía de clases: continuación


Mira el código en el editor nuevamente. 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.
import time
class Vehiculo:
def cambiardireccion(izquierda, on):
Módulo 6
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)

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.
Módulo 6
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.
Módulo 6
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

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):
Módulo 6
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)

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.
Módulo 6
 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 D(A, B):
pass class C(A): pass
class B(A): pass d = D()
Módulo 6
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.

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.
Módulo 6
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).
Módulo 6
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. Su salida es:
Todo salió bien División fallida
Es el momento de decir adiós Es el momento de decir adiós
0.5 None

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))
Módulo 6

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.
Módulo 6
try:
i = int("Hola!")
except Exception as e:
print(e)
print(e.__str__())

Todas las excepciones integradas de Python forman una jerarquía de clases.


Analiza el código en el editor.
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__.


Módulo 6
 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
Módulo 6
| | | +---ConnectionAbortedError
| | | +---ConnectionRefusedError
| | | +---ConnectionResetError
| | +---BlockingIOError
| | +---ChildProcessError
| | +---FileExistsError
| | +---FileNotFoundError
| | +---IsADirectoryError
| | +---NotADirectoryError
| | +---InterruptedError
| | +---PermissionError
| | +---ProcessLookupError
| | +---TimeoutError
| | +---UnsupportedOperation
| | +---herror
| | +---gaierror
| | +---timeout
| | +---Error
Módulo 6
| | | +---SameFileError
| | +---SpecialFileError
| | +---ExecError
| | +---ReadError
| +---EOFError
| +---RuntimeError
| | +---RecursionError
| | +---NotImplementedError
| | +---_DeadlockError
| | +---BrokenBarrierError
| +---NameError
| | +---UnboundLocalError
| +---AttributeError
| +---SyntaxError
| | +---IndentationError
| | | +---TabError
| +---LookupError
| | +---IndexError
Módulo 6
| | +---KeyError
| | +---CodecRegistryError
| +---ValueError
| | +---UnicodeError
| | | +---UnicodeEncodeError
| | | +---UnicodeDecodeError
| | | +---UnicodeTranslateError
| | +---UnsupportedOperation
| +---AssertionError
| +---ArithmeticError
| | +---FloatingPointError
| | +---OverflowError
| | +---ZeroDivisionError
| +---SystemError
| | +---CodecRegistryError
| +---ReferenceError
| +---BufferError
| +---MemoryError
Módulo 6
| +---Warning
| | +---UserWarning
| | +---DeprecationWarning
| | +---PendingDeprecationWarning
| | +---SyntaxWarning
| | +---RuntimeWarning
| | +---FutureWarning
| | +---ImportWarning
| | +---UnicodeWarning
| | +---BytesWarning
| | +---ResourceWarning
| +---error
| +---Verbose
| +---Error
| +---TokenError
| +---StopTokenizing
| +---Empty
| +---Full
Módulo 6
| +---_OptionError
| +---TclError
| +---SubprocessError
| | +---CalledProcessError
| | +---TimeoutExpired
| +---Error
| | +---NoSectionError
| | +---DuplicateSectionError
| | +---DuplicateOptionError
| | +---NoOptionError
| | +---InterpolationError
| | | +---InterpolationMissingOptionError
| | | +---InterpolationSyntaxError
| | | +---InterpolationDepthError
| | +---ParsingError
| | | +---MissingSectionHeaderError
| +---InvalidConfigType
| +---InvalidConfigSet
Módulo 6
| +---InvalidFgBg
| +---InvalidTheme
| +---EndOfBlock
| +---BdbQuit
| +---error
| +---_Stop
| +---PickleError
| | +---PicklingError
| | +---UnpicklingError
| +---_GiveupOnSendfile
| +---error
| +---LZMAError
| +---RegistryError
| +---ErrorDuringImport
+---GeneratorExit
+---SystemExit
+---KeyboardInterrupt
Módulo 6
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)

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.
Módulo 6
Hemos preparado una función simple para imprimir la propiedad args de una manera
elegante, puedes ver la función en el editor.

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')
def printargs(args):
lng = len(args)
if lng == 0:
Módulo 6
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:

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.
Módulo 6
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:

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.
Módulo 6
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).
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:
Módulo 6
doTheDivision(mode)
except MyZeroDivisionError:
print('Mi división entre cero')
except ZeroDivisionError:
print('División entre cero original')

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
Módulo 6
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.
class PizzaError(Exception):
def __init__(self, pizza, mensaje):
Exception.__init__(mensaje)
Módulo 6
self.pizza = pizza
class DemasiadoQuesoError(PizzaError):
def __init__(self, pizza, queso, mensaje):
PizzaError._init__(self, pizza, mensaje)
self.queso = queso

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.

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
Módulo 6
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!")
Módulo 6
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.
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']:
Módulo 6
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:

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)
Módulo 6
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.
for i in range(5):
print(i)

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).
Módulo 6
 __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, Fib 3 =
2, Fib4 = 3, Fib5 = 5, y así sucesivamente).

Vamos a ver el código:


 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
Módulo 6
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__ __next__ __next__ __next__ __next__ __next__
__iter__ 1 3 8 21 55
__next__ __next__ __next__ __next__ __next__ __next__
1 2 5 13 34
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.

class Fib:
Módulo 6
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)

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.


Módulo 6
Echa un vistazo al código en el editor.

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.

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
Módulo 6
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)

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.
Módulo 6
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.
Módulo 6
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): yield i print(v)
for i in range(n): for v in fun(5):
¿Puedes adivinar la salida?

Revisar
0 1 2 3 4
Módulo 6

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.


¿Puedes adivinar la salida? Ejecuta el código para verificar tus conjeturas.
Los generadores también pueden usarse dentro de listas de comprensión, como aqui:
def potenciasDe2(n): potencia *= 2
potencia = 1 t = [x for x in potenciasDe2(5)]
for i in range(n): print(t)
yield potencia
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 *= 2
potencia = 1 t = list(potenciasDe2(3))
for i in range(n): print(t)
yield potencia
Nuevamente, intenta predecir el resultado y ejecuta el código para verificar tus predicciones.
Módulo 6
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*= 2
potencia= 1 for i in range(20):
for i in range(n): if i in potenciasDe2(4):
yield potencia 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): n = p + pp

p = pp = 1 pp, p = p, n

for i in range(n): yield n

if i in [0, 1]: fibs = list(Fib(10))

yield 1 print(fibs)

else:

Adivina la salida (una lista) producida por el generador y ejecuta el código para verificar si
tenías razón.
Módulo 6
def potenciasDe2(n):
potencia = 1
for i in range(n):
yield potencia
potencia *= 2
for v in potenciasDe2(8):
print(v)

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.
Módulo 6
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:
[1, 10, 100, 1000, 10000, 100000]
Ejecuta el código para verificar si tenemos razón.
listaUno = []
for ex in range(6):
listaUno.append(10 ** ex)
listaDos = [10 ** ex for ex in range(6)]
print(listaUno)
print(listaDos)

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.
Módulo 6
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.

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.
lst = []
for x in range(10):
lst.append(1 if x % 2 == 0 else 0)
print(lst)

Más sobre comprensión de listas: continuación


Mira el ejemplo en el editor.
Módulo 6
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?
Módulo 6
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.
Módulo 6
lst = [1 if x % 2 == 0 else 0 for x in range(10)]
print(lst)

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
Módulo 6
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 for a in range(-2, 3):
cuadrado = lambda x : x * x print(cuadrado(a), end=" ")
potencia = lambda x, y : x ** y 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
Módulo 6
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.

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:
Módulo 6
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.


Módulo 6
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.
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)

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.
Módulo 6
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.
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.
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()
Módulo 6

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.

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]
from random import seed, randint
seed()
Módulo 6
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)

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): var = 1 print(var)
loc = par exterior(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.

Hay un elemento completamente nuevo - una función (llamada interior) dentro de otra
función (llamada exterior).
Módulo 6
¿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:
1
La función devuelta durante la invocación de exterior() es un cierre.

def exterior(par):
loc = par
def interior():
return loc
Módulo 6
return interior
var = 1
fun = exterior(var)
print(fun())

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): return loc fun = exterior(var)


loc = par return interior print(fun()))
def interior(): var = 1
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().

Esto significa que el cierre no solo utiliza el ambiente congelado, sino que también
puede modificar su comportamiento utilizando valores tomados del exterior.
Módulo 6
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
Realiza tus propias pruebas.
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))
Módulo 6

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.
Módulo 6
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
Módulo 6
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

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.
Módulo 6
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,
Módulo 6
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
Módulo 6
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
Módulo 6
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.
Módulo 6
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.
Módulo 6
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.


Módulo 6
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.

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.
Módulo 6
 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.
Módulo 6
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
Módulo 6
 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.
Módulo 6

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)
Módulo 6
¿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.


Módulo 6
Vamos a analizarlos:

 sys.stdin
 stdin (significa entrada estándar).
 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.
 La función bien conocida input() lee datos de stdin por default.
 sys.stdout
 stdout (significa salida estándar).
 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.
 La función bien conocida print() envía los datos al stream stdout.
 sys.stderr
 stderr (significa salida de error estándar).
 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.
 No hemos presentado ningún método para enviar datos a este stream (lo haremos
pronto, lo prometemos).
 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.
Módulo 6
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.
Módulo 6
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: except IOError as exc:
# operaciones con streams 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.
Módulo 6
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:
Módulo 6
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:
Módulo 6
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.).
Módulo 6
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.
Módulo 6
stream = open("tzop.txt", "rt", encoding = "utf-8") # se abre el archivo
tzop.txt en modo lectura, devolviéndolo como un objeto de archivo
print(stream.read()) # se imprime el contenido del archivo

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:
Módulo 6
Lo hermoso es mejor que lo feo. Simple es mejor que complejo.
Explícito es mejor que implícito. Complejo es mejor que complicado.
Ahora observa el código en el editor y analicémoslo.
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.
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:
Módulo 6
print("Se produjo un error de E/S: ", strerr(e.errno))

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?
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.
from os import strerror
try:
cnt = 0
Módulo 6
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))

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.
Módulo 6
Hagámos uso de ello. Observa el código en el editor.

Como puedes ver, la idea general es exactamente la misma que en los dos ejemplos
anteriores.
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))
Módulo 6

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().

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.
Módulo 6
Observa el código en el editor. Lo hemos modificado para mostrarte cómo
usar readlines().

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.
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:
Módulo 6
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))

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.
Módulo 6
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))

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).
Módulo 6
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.

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 #3 línea #5 línea #7 línea #9
línea #2 línea #4 línea #6 línea #8 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.
from os import strerror
try:
fo = open('newtext.txt', 'wt') #un nuevo archivo (newtext.txt) es creado
for i in range(10):
Módulo 6
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))

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.
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")
from os import strerror
try:
Módulo 6
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))

¿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).
Módulo 6
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.

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.
Módulo 6
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.
data = bytearray(10)
for i in range(len(data)):
data[i] = 10 - i
for b in data:
print(hex(b))

Bytearrays: continuación
Entonces, ¿cómo escribimos un arreglo de bytes en un archivo binario?
Observa el código en el editor. Analicémoslo:

 Primero, inicializamos bytearray con valores a partir de 10; si deseas que el contenido del
archivo sea claramente legible, reemplaza el 10con 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.
Módulo 6
 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).
Módulo 6
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.


Módulo 6
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))
# ingresa aquí el código que lee los bytes del stream

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.
Módulo 6
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.
from os import strerror
data = bytearray(10)
for i in range(len(data)):
data[i] = 10 + i
try:
Módulo 6
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))
# ingresa aquí el código que lee los bytes del stream

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:
Módulo 6
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.
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))
# ingresa aquí el código que lee los bytes del stream

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.
Módulo 6
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:

 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.
Módulo 6
 Línea 24: lee el siguiente fragmento de archivo.
 Las líneas 29 a la 31: limpieza final: el trabajo está hecho.
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)
Módulo 6

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.


Módulo 6
 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.
Módulo 6

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.
Módulo 6
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.


Módulo 6

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 John Smith 2 Andrew Cox 1.5
Anna Boleyn 4.5 Anna Boleyn 11
Tu tarea es escribir un programa que:
Módulo 6
 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